Skip to content

Commit 5f74aef

Browse files
committed
feat: add secondaryContent to Header
1 parent c7e4aef commit 5f74aef

9 files changed

Lines changed: 95 additions & 13 deletions

File tree

‎pages/header/permutations.page.tsx‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
import React from 'react';
44

5-
import { Button, Link, Spinner } from '~components';
5+
import { Button, Link, Select, Spinner } from '~components';
66
import Header, { HeaderProps } from '~components/header';
77

88
import createPermutations from '../utils/permutations';
@@ -30,6 +30,29 @@ const permutations = createPermutations<HeaderProps>([
3030
null,
3131
],
3232
},
33+
{
34+
variant: ['h1', 'h2', 'h3'],
35+
secondaryContent: [
36+
<Select
37+
key="select"
38+
ariaLabel="Time range"
39+
selectedOption={{ value: '1h', label: 'Last hour' }}
40+
options={[{ value: '1h', label: 'Last hour' }]}
41+
onChange={() => {}}
42+
/>,
43+
],
44+
counter: ['(1/10)', undefined],
45+
description: [
46+
'A description text with some content to simulate a text shown below the title of this header.',
47+
null,
48+
],
49+
actions: [
50+
<Button key="button" variant="primary">
51+
Create Resource
52+
</Button>,
53+
null,
54+
],
55+
},
3356
]);
3457

3558
export default function HeaderPermutations() {

‎src/__tests__/snapshot-tests/__snapshots__/documenter.test.ts.snap‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16319,6 +16319,11 @@ to display resource counters in table and cards components.",
1631916319
"isDefault": false,
1632016320
"name": "info",
1632116321
},
16322+
{
16323+
"description": "Secondary content displayed next to the header text.",
16324+
"isDefault": false,
16325+
"name": "secondaryContent",
16326+
},
1632216327
],
1632316328
"releaseStatus": "stable",
1632416329
}
@@ -43632,6 +43637,19 @@ The dismiss button is only rendered when the \`dismissible\` property is set to
4363243637
],
4363343638
},
4363443639
},
43640+
{
43641+
"name": "findSecondaryContent",
43642+
"parameters": [],
43643+
"returnType": {
43644+
"isNullable": true,
43645+
"name": "ElementWrapper",
43646+
"typeArguments": [
43647+
{
43648+
"name": "HTMLElement",
43649+
},
43650+
],
43651+
},
43652+
},
4363543653
],
4363643654
"name": "HeaderWrapper",
4363743655
},
@@ -55065,6 +55083,14 @@ The dismiss button is only rendered when the \`dismissible\` property is set to
5506555083
"name": "ElementWrapper",
5506655084
},
5506755085
},
55086+
{
55087+
"name": "findSecondaryContent",
55088+
"parameters": [],
55089+
"returnType": {
55090+
"isNullable": false,
55091+
"name": "ElementWrapper",
55092+
},
55093+
},
5506855094
],
5506955095
"name": "HeaderWrapper",
5507055096
},

‎src/__tests__/snapshot-tests/__snapshots__/test-utils-selectors.test.tsx.snap‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ exports[`test-utils selectors 1`] = `
378378
"awsui_heading-text_2qdw9",
379379
"awsui_info_2qdw9",
380380
"awsui_root_2qdw9",
381+
"awsui_secondary-content_2qdw9",
381382
],
382383
"help-panel": [
383384
"awsui_content_1d237",

‎src/expandable-section/expandable-section-header.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ const ExpandableHeaderTextWrapper = ({
410410
actions={actions}
411411
headingTagOverride={hideText ? 'div' : headingTagOverride}
412412
__inExpandableSection={true}
413-
__additionalTitleContent={secondaryContent ? <span id={describedById}>{secondaryContent}</span> : undefined}
413+
secondaryContent={secondaryContent ? <span id={describedById}>{secondaryContent}</span> : undefined}
414414
>
415415
{headerButton}
416416
</InternalHeader>

‎src/header/__tests__/header.test.tsx‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,37 @@ test('renders everything provided', () => {
4242
expect(wrapper.findActions()!.find('button')!.getElement()).toHaveTextContent('Click me');
4343
});
4444

45+
describe('secondary content', () => {
46+
test('is not rendered by default', () => {
47+
const { wrapper } = renderHeader(<Header>Test title</Header>);
48+
expect(wrapper.findSecondaryContent()).toBeNull();
49+
});
50+
51+
test.each(['h1', 'h2', 'h3', 'awsui-h1-sticky'] as const)('renders outside the heading element for %s', variant => {
52+
const { wrapper } = renderHeader(
53+
<Header variant={variant} secondaryContent={<button>Filter</button>}>
54+
Test title
55+
</Header>
56+
);
57+
const secondaryContent = wrapper.findSecondaryContent()!.getElement();
58+
expect(secondaryContent).toHaveTextContent('Filter');
59+
expect(secondaryContent.closest('h1,h2,h3,h4,h5,h6')).toBeNull();
60+
});
61+
62+
test('renders between the title and the actions', () => {
63+
const { wrapper } = renderHeader(
64+
<Header info="Info" secondaryContent={<button>Filter</button>} actions={<button>Create</button>}>
65+
Test title
66+
</Header>
67+
);
68+
const info = wrapper.findInfo()!.getElement();
69+
const secondaryContent = wrapper.findSecondaryContent()!.getElement();
70+
const actions = wrapper.findActions()!.getElement();
71+
expect(info.compareDocumentPosition(secondaryContent) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
72+
expect(secondaryContent.compareDocumentPosition(actions) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
73+
});
74+
});
75+
4576
test('renders arbitrary counter content', () => {
4677
const { wrapper } = renderHeader(<Header counter={<Spinner />} />);
4778
expect(wrapper.findCounter()).not.toBeNull();

‎src/header/interfaces.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ export interface HeaderProps extends BaseComponentProps {
4343
* Area next to the heading to display an Info link.
4444
*/
4545
info?: React.ReactNode;
46+
/**
47+
* Secondary content displayed next to the header text.
48+
*/
49+
secondaryContent?: React.ReactNode;
4650
}
4751

4852
export namespace HeaderProps {

‎src/header/internal.tsx‎

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ interface InternalHeaderProps extends SomeRequired<HeaderProps, 'variant'>, Inte
2727
__headingTagRef?: MutableRefObject<HTMLHeadingElement | null>;
2828
__headingTagTabIndex?: number;
2929
__inExpandableSection?: boolean;
30-
/**
31-
* Rendered in the title row but outside the heading tag, so content beside the title is not
32-
* nested inside the heading element.
33-
*/
34-
__additionalTitleContent?: React.ReactNode;
3530
}
3631

3732
export default function InternalHeader({
@@ -42,12 +37,12 @@ export default function InternalHeader({
4237
counter,
4338
description,
4439
info,
40+
secondaryContent,
4541
__internalRootRef,
4642
__disableActionsWrapping,
4743
__headingTagRef,
4844
__headingTagTabIndex,
4945
__inExpandableSection,
50-
__additionalTitleContent,
5146
...restProps
5247
}: InternalHeaderProps) {
5348
const isMobile = useMobile();
@@ -124,9 +119,7 @@ export default function InternalHeader({
124119
</InfoLinkLabelContext.Provider>
125120
)}
126121
</div>
127-
{__additionalTitleContent && (
128-
<div className={styles['additional-title-content']}>{__additionalTitleContent}</div>
129-
)}
122+
{secondaryContent && <div className={styles['secondary-content']}>{secondaryContent}</div>}
130123
{actions && (
131124
<div
132125
className={clsx(

‎src/header/styles.scss‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
}
131131
}
132132

133-
.additional-title-content {
133+
.secondary-content {
134134
display: inline-flex;
135135
align-items: center;
136136
flex-shrink: 0;
@@ -189,7 +189,7 @@
189189

190190
// With secondary content beside the heading, center both against a constant-height line so their
191191
// alignment no longer shifts when actions wrap to a second row.
192-
.main:has(> .additional-title-content) > .title-variant-h2.title-in-expandable-section.refresh {
192+
.main:has(> .secondary-content) > .title-variant-h2.title-in-expandable-section.refresh {
193193
padding-block-start: 0;
194194
min-block-size: awsui.$size-vertical-input;
195195
display: flex;

‎src/test-utils/dom/header/index.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ export default class HeaderWrapper extends ComponentWrapper {
2626
findActions(): ElementWrapper | null {
2727
return this.findByClassName(styles.actions);
2828
}
29+
30+
findSecondaryContent(): ElementWrapper | null {
31+
return this.findByClassName(styles['secondary-content']);
32+
}
2933
}

0 commit comments

Comments
 (0)