Skip to content

Commit 572eac2

Browse files
committed
Revert "Remove headerBefore and ariaLabel"
This reverts commit fca330f.
1 parent b042ad2 commit 572eac2

File tree

9 files changed

+152
-26
lines changed

9 files changed

+152
-26
lines changed

pages/app-layout/split-panel-with-custom-header.page.tsx

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
import React, { useContext, useEffect, useState } from 'react';
3+
import React, { useContext, useEffect, useRef, useState } from 'react';
44

55
import AppLayout, { AppLayoutProps } from '~components/app-layout';
6+
import Badge from '~components/badge';
7+
import Box from '~components/box';
68
import Button from '~components/button';
79
import FormField from '~components/form-field';
810
import Header from '~components/header';
@@ -22,22 +24,68 @@ import * as toolsContent from './utils/tools-content';
2224
type SplitPanelDemoContext = React.Context<
2325
AppContextType<{
2426
actionsAsLinks: boolean;
27+
ariaLabel?: string;
2528
description?: string;
29+
editableHeader: boolean;
2630
headerText?: string;
2731
renderActions: boolean;
32+
renderBadge: boolean;
2833
renderInfoLink: boolean;
2934
splitPanelOpen: boolean;
3035
splitPanelPosition: AppLayoutProps.SplitPanelPreferences['position'];
3136
}>
3237
>;
3338

39+
function EditableHeader({ onChange, value }: { onChange: (text: string) => void; value: string }) {
40+
const [internalValue, setInternalValue] = useState(value);
41+
const [editing, setEditing] = useState(false);
42+
const inputRef = useRef<HTMLInputElement>(null);
43+
useEffect(() => {
44+
if (editing) {
45+
inputRef.current?.focus();
46+
}
47+
}, [editing]);
48+
49+
return (
50+
<Box display="inline-block">
51+
<SpaceBetween direction="horizontal" size="xxs" alignItems="center">
52+
{editing ? (
53+
<>
54+
<Input value={internalValue} onChange={({ detail }) => setInternalValue(detail.value)} ref={inputRef} />
55+
<Button
56+
variant="icon"
57+
iconName="check"
58+
onClick={() => {
59+
onChange(internalValue);
60+
setEditing(false);
61+
}}
62+
/>
63+
<Button variant="icon" iconName="close" onClick={() => setEditing(false)} />
64+
</>
65+
) : (
66+
<>
67+
<Box variant="h3" tagOverride="h2" display="inline" margin={{ vertical: 'n' }} padding={{ vertical: 'n' }}>
68+
{value}
69+
</Box>
70+
<Button variant="inline-icon" iconName="edit" onClick={() => setEditing(true)}></Button>
71+
</>
72+
)}
73+
</SpaceBetween>
74+
</Box>
75+
);
76+
}
77+
3478
export default function () {
3579
const { urlParams, setUrlParams } = useContext(AppContext as SplitPanelDemoContext);
3680
const [toolsOpen, setToolsOpen] = useState(false);
3781

3882
// Initialize the header to a default value if not set.
39-
// eslint-disable-next-line react-hooks/exhaustive-deps
40-
useEffect(() => setUrlParams({ ...urlParams, headerText: urlParams.headerText || 'Header text' }), []);
83+
useEffect(() => {
84+
if (!urlParams.editableHeader) {
85+
setUrlParams({ ...urlParams, headerText: urlParams.headerText || 'Header text' });
86+
}
87+
// eslint-disable-next-line react-hooks/exhaustive-deps
88+
}, []);
4189

4290
return (
4391
<ScreenshotArea gutters={false}>
@@ -59,7 +107,7 @@ export default function () {
59107
onToolsChange={({ detail }) => setToolsOpen(detail.open)}
60108
splitPanel={
61109
<SplitPanel
62-
header={urlParams.headerText || ''}
110+
header={(!urlParams.editableHeader && urlParams.headerText) || ''}
63111
i18nStrings={splitPaneli18nStrings}
64112
headerActions={
65113
urlParams.renderActions &&
@@ -72,6 +120,21 @@ export default function () {
72120
</SpaceBetween>
73121
))
74122
}
123+
headerBefore={
124+
(urlParams.renderBadge || urlParams.editableHeader) && (
125+
<>
126+
{urlParams.renderBadge && <Badge>Badge</Badge>}
127+
{urlParams.editableHeader && (
128+
<Box display="inline-block" margin={{ left: urlParams.renderBadge ? 'xs' : 'n' }}>
129+
<EditableHeader
130+
value={urlParams.headerText || ''}
131+
onChange={value => setUrlParams({ ...urlParams, headerText: value })}
132+
/>
133+
</Box>
134+
)}
135+
</>
136+
)
137+
}
75138
headerDescription={urlParams.description}
76139
headerInfo={
77140
urlParams.renderInfoLink && (
@@ -80,6 +143,7 @@ export default function () {
80143
</Link>
81144
)
82145
}
146+
ariaLabel={urlParams.ariaLabel}
83147
>
84148
<ScrollableDrawerContent />
85149
</SplitPanel>
@@ -92,13 +156,25 @@ export default function () {
92156
</Header>
93157
</div>
94158
<SpaceBetween size="l">
95-
<Toggle
96-
checked={urlParams.renderInfoLink}
97-
onChange={({ detail }) => setUrlParams({ ...urlParams, renderInfoLink: detail.checked })}
98-
>
99-
With info link
100-
</Toggle>
101159
<SpaceBetween direction="horizontal" size="xl">
160+
<Toggle
161+
checked={urlParams.renderBadge}
162+
onChange={({ detail }) => setUrlParams({ ...urlParams, renderBadge: detail.checked })}
163+
>
164+
With badge
165+
</Toggle>
166+
<Toggle
167+
checked={urlParams.editableHeader}
168+
onChange={({ detail }) => setUrlParams({ ...urlParams, editableHeader: detail.checked })}
169+
>
170+
Editable header text
171+
</Toggle>
172+
<Toggle
173+
checked={urlParams.renderInfoLink}
174+
onChange={({ detail }) => setUrlParams({ ...urlParams, renderInfoLink: detail.checked })}
175+
>
176+
With info link
177+
</Toggle>
102178
<Toggle
103179
checked={urlParams.renderActions}
104180
onChange={({ detail }) => setUrlParams({ ...urlParams, renderActions: detail.checked })}
@@ -126,6 +202,12 @@ export default function () {
126202
onChange={({ detail }) => setUrlParams({ ...urlParams, description: detail.value })}
127203
/>
128204
</FormField>
205+
<FormField label="ARIA label">
206+
<Input
207+
value={urlParams.ariaLabel || ''}
208+
onChange={({ detail }) => setUrlParams({ ...urlParams, ariaLabel: detail.value })}
209+
/>
210+
</FormField>
129211
<Containers />
130212
</SpaceBetween>
131213
</>

src/split-panel/__tests__/header.test.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ describe('Split panel: Header slots', () => {
1515
expect(wrapper!.findHeaderActions()!.getElement()).toHaveTextContent('Action');
1616
});
1717

18+
test('renders headerBefore', () => {
19+
const { wrapper } = renderSplitPanel({
20+
props: { headerBefore: <span>Before</span> },
21+
});
22+
expect(wrapper!.findHeaderBefore()).not.toBeNull();
23+
expect(wrapper!.findHeaderBefore()!.getElement()).toHaveTextContent('Before');
24+
});
25+
1826
test('renders headerDescription', () => {
1927
const { wrapper } = renderSplitPanel({
2028
props: { headerDescription: 'Header description' },
@@ -34,6 +42,7 @@ describe('Split panel: Header slots', () => {
3442
test('does not render header properties when not provided', () => {
3543
const { wrapper } = renderSplitPanel();
3644
expect(wrapper!.findHeaderActions()).toBeNull();
45+
expect(wrapper!.findHeaderBefore()).toBeNull();
3746
expect(wrapper!.findHeaderDescription()).toBeNull();
3847
expect(wrapper!.findHeaderInfo()).toBeNull();
3948
});

src/split-panel/bottom.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export function SplitPanelContentBottom({
3333
children,
3434
appLayoutMaxWidth,
3535
panelHeaderId,
36+
ariaLabel,
3637
onToggle,
3738
hasCustomElements,
3839
}: SplitPanelContentBottomProps) {
@@ -101,7 +102,12 @@ export function SplitPanelContentBottom({
101102
{closeBehavior === 'hide' && !isOpen ? null : (
102103
<>
103104
{isOpen && <div className={styles['slider-wrapper-bottom']}>{resizeHandle}</div>}
104-
<div className={styles['drawer-content-bottom']} aria-labelledby={panelHeaderId} role="region">
105+
<div
106+
className={styles['drawer-content-bottom']}
107+
aria-labelledby={panelHeaderId}
108+
aria-label={ariaLabel}
109+
role="region"
110+
>
105111
<div className={clsx(styles['pane-header-wrapper-bottom'], centeredMaxWidthClasses)} ref={headerRef}>
106112
{header}
107113
</div>

src/split-panel/implementation.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ export function SplitPanelImplementation({
3636
hidePreferencesButton,
3737
closeBehavior,
3838
i18nStrings = {},
39+
ariaLabel,
3940
headerActions,
41+
headerBefore,
4042
headerDescription,
4143
headerInfo,
4244
...restProps
@@ -91,26 +93,27 @@ export function SplitPanelImplementation({
9193
[globalVars.stickyVerticalBottomOffset]: bottomOffset,
9294
};
9395

94-
const panelHeaderId = useUniqueId('split-panel-header');
96+
const panelHeaderUniqueId = useUniqueId('split-panel-header');
97+
const panelHeaderId = ariaLabel ? undefined : panelHeaderUniqueId;
9598

96-
const hasCustomElements = !!headerActions || !!headerInfo;
99+
const hasCustomElements = !!headerActions || !!headerBefore || !!headerInfo;
97100

98101
const showDescription = headerDescription && isOpen;
99102

100103
const wrappedHeader = (
101104
<div className={clsx(styles.header, isToolbar && styles['with-toolbar'])} style={appLayoutMaxWidth}>
102105
<div className={styles['header-main-row']}>
103106
<div className={styles['header-main-content']}>
104-
<div className={styles['header-text-and-info']}>
105-
<h2
106-
className={clsx(
107-
styles['header-text'],
108-
testUtilStyles['header-text'],
109-
!!headerInfo && styles['with-info']
107+
<div className={styles['header-tag-and-info']}>
108+
<h2 className={clsx(styles['header-tag'], !!headerInfo && styles['with-info'])} id={panelHeaderId}>
109+
{headerBefore && (
110+
<span className={clsx(styles['header-before'], testUtilStyles['header-before'])}>{headerBefore}</span>
110111
)}
111-
id={panelHeaderId}
112-
>
113-
{header}
112+
{header && (
113+
<div className={clsx(styles['header-text'], testUtilStyles['header-text'])} id={panelHeaderId}>
114+
{header}
115+
</div>
116+
)}{' '}
114117
</h2>
115118
{headerInfo && (
116119
<span className={clsx(styles['header-info'], testUtilStyles['header-info'])}>{headerInfo}</span>
@@ -246,6 +249,7 @@ export function SplitPanelImplementation({
246249
toggleRef={refs.toggle}
247250
header={wrappedHeader}
248251
panelHeaderId={panelHeaderId}
252+
ariaLabel={ariaLabel}
249253
closeBehavior={closeBehavior}
250254
>
251255
{children}
@@ -264,6 +268,7 @@ export function SplitPanelImplementation({
264268
header={wrappedHeader}
265269
panelHeaderId={panelHeaderId}
266270
appLayoutMaxWidth={appLayoutMaxWidth}
271+
ariaLabel={ariaLabel}
267272
closeBehavior={closeBehavior}
268273
hasCustomElements={hasCustomElements}
269274
>

src/split-panel/interfaces.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ export interface SplitPanelProps extends BaseComponentProps {
3434
*/
3535
i18nStrings?: SplitPanelProps.I18nStrings;
3636

37+
/**
38+
* ARIA label for the panel. Use this if the value passed in the `header` property is not descriptive as a label for the panel.
39+
*/
40+
ariaLabel?: string;
41+
3742
/**
3843
* Actions for the header. Available only if you specify the `header` property.
3944
*/
@@ -48,6 +53,11 @@ export interface SplitPanelProps extends BaseComponentProps {
4853
* The area next to the heading, used to display an Info link.
4954
*/
5055
headerInfo?: React.ReactNode;
56+
57+
/**
58+
* Content displayed before the header text.
59+
*/
60+
headerBefore?: React.ReactNode;
5161
}
5262

5363
export namespace SplitPanelProps {
@@ -73,7 +83,8 @@ export interface SplitPanelContentProps {
7383
isOpen?: boolean;
7484
splitPanelRef?: React.Ref<any>;
7585
cappedSize: number;
76-
panelHeaderId: string;
86+
panelHeaderId?: string;
87+
ariaLabel?: string;
7788
resizeHandle: React.ReactNode;
7889
header: React.ReactNode;
7990
children: React.ReactNode;

src/split-panel/side.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function SplitPanelContentSide({
3232
cappedSize,
3333
openButtonAriaLabel,
3434
panelHeaderId,
35+
ariaLabel,
3536
onToggle,
3637
closeBehavior,
3738
}: SplitPanelContentSideProps) {
@@ -64,6 +65,7 @@ export function SplitPanelContentSide({
6465
}}
6566
onClick={() => !isOpen && onToggle()}
6667
aria-labelledby={panelHeaderId}
68+
aria-label={ariaLabel}
6769
role="region"
6870
>
6971
{isOpen ? (

src/split-panel/styles.scss

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ $app-layout-drawer-width: calc(#{awsui.$space-layout-toggle-diameter} + 2 * #{aw
254254
justify-content: space-between;
255255
}
256256

257-
&-text-and-info {
257+
&-tag-and-info {
258258
$vertical-margin: calc(#{awsui.$space-scaled-xxs} + 1px);
259259
margin-block-start: $vertical-margin;
260260
min-block-size: calc(#{awsui.$font-panel-header-line-height} + #{$vertical-margin});
@@ -263,14 +263,16 @@ $app-layout-drawer-width: calc(#{awsui.$space-layout-toggle-diameter} + 2 * #{aw
263263
line-height: awsui.$line-height-body-s;
264264
}
265265

266+
&-tag,
267+
&-before,
266268
&-text,
267269
&-info {
268270
display: inline;
269271
}
270272

271-
&-text {
273+
&-tag {
274+
$vertical-margin: calc(#{awsui.$space-scaled-xxs} + 1px);
272275
@include styles.font-panel-header;
273-
flex-grow: 1;
274276
padding-block: 0;
275277
padding-inline: 0;
276278
margin-inline: 0;
@@ -280,6 +282,10 @@ $app-layout-drawer-width: calc(#{awsui.$space-layout-toggle-diameter} + 2 * #{aw
280282
}
281283
}
282284

285+
&-before + &-text {
286+
margin-inline-start: awsui.$space-scaled-xs;
287+
}
288+
283289
&-description {
284290
color: awsui.$color-text-heading-secondary;
285291
@include styles.font-body-m;

src/split-panel/test-classes/styles.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
.root,
77
.header-actions,
8+
.header-before,
89
.header-description,
910
.header-info,
1011
.header-text,

src/test-utils/dom/split-panel/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export default class SplitPanelWrapper extends ComponentWrapper {
3434
return this.findByClassName(testUtilStyles['header-actions']);
3535
}
3636

37+
findHeaderBefore() {
38+
return this.findByClassName(testUtilStyles['header-before']);
39+
}
40+
3741
findHeaderDescription() {
3842
return this.findByClassName(testUtilStyles['header-description']);
3943
}

0 commit comments

Comments
 (0)