Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
97c03bf
Add new props
jperals Aug 6, 2025
7834078
Add dev page
jperals Aug 6, 2025
bd9f217
Use provided ARIA label
jperals Aug 6, 2025
b7db005
Implementation
jperals Aug 6, 2025
9df4478
Implementation
jperals Aug 6, 2025
30e96c8
Adjust spacings
jperals Aug 6, 2025
422c132
Reduce clickable area when custom elements are present
jperals Aug 6, 2025
27e8d61
Fix hover effect
jperals Aug 6, 2025
5985bcc
Fix spacings
jperals Aug 6, 2025
7825a4f
Fine tune spacings
jperals Aug 6, 2025
57e2023
Place description below buttons
jperals Aug 6, 2025
104923d
Do not render header if not passed in
jperals Aug 6, 2025
7035be5
Spacing adjustments
jperals Aug 7, 2025
a8d9eec
Initialize the header to a default value if not set
jperals Aug 7, 2025
e0375f9
Place headerBefore slot inside heading tag
jperals Aug 7, 2025
033c18d
Persist split panel open state
jperals Aug 8, 2025
813368e
Editable header text
jperals Aug 8, 2025
3add9c8
Adjust spacings
jperals Aug 8, 2025
52e595d
Add test utils
jperals Aug 8, 2025
bdc310e
Add unit test coverage
jperals Aug 8, 2025
ca9fca0
Remove unnecessary CSS rules
jperals Aug 8, 2025
8ae5f6d
Remove headerBefore and ariaLabel
jperals Aug 8, 2025
0e37d85
Refactor class names
jperals Aug 8, 2025
c5dbc48
Remove leftover metadata prop
jperals Aug 8, 2025
43e4429
Update snapshots
jperals Aug 8, 2025
36213e8
Adjust spacings
jperals Aug 8, 2025
5c691f7
Adjust line height
jperals Aug 9, 2025
665b1e7
Adjust header height with vertical-align
jperals Aug 9, 2025
fd4577c
Revert "Adjust header height with vertical-align"
jperals Aug 9, 2025
057b414
Line height fix
jperals Aug 9, 2025
7802dc8
Spacing adjustments
jperals Aug 9, 2025
7b5f51e
Remove line-height workaround
jperals Aug 11, 2025
751b581
Add actionsAsLinks prop to dev page
jperals Aug 11, 2025
663e417
Spacing adjustments
jperals Aug 11, 2025
74270de
Update test utils selectors snapshots
jperals Aug 8, 2025
95d7cdd
Update test util class names
jperals Aug 8, 2025
e46aba2
Update documenter snapshots
jperals Aug 8, 2025
e28282d
Kepp panelHeaderId mandatory
jperals Aug 11, 2025
65e894d
Refactor class names
jperals Aug 11, 2025
7e6b6e2
Add integ coverage
jperals Aug 11, 2025
87c711c
line-height workaround
jperals Aug 11, 2025
ccf59fd
Add comment
jperals Aug 11, 2025
742ac98
Move line-height workaround to child node
jperals Aug 11, 2025
3fc76e3
Fix metadata prop
jperals Aug 8, 2025
9e6af19
Revert "Remove headerBefore and ariaLabel"
jperals Aug 11, 2025
298e411
Layout adjustments
jperals Aug 11, 2025
2aee5d3
Remove duplicated id
jperals Aug 12, 2025
c699533
Fine tune dev page
jperals Aug 12, 2025
aaffe49
Layout adjustments
jperals Aug 12, 2025
7986114
Fine tune dev page
jperals Aug 12, 2025
6b32d13
Fine tune dev page
jperals Aug 12, 2025
bcc314b
More adjustments
jperals Aug 12, 2025
0da8a3f
Fine tune dev page
jperals Aug 13, 2025
1a02bff
Update test util snapshots
jperals Aug 13, 2025
f071b89
Add integ coverage
jperals Aug 13, 2025
b83a2da
Do not let actions slot wrap
jperals Aug 13, 2025
e011a41
Fix slot rendering conditions in dev page
jperals Aug 13, 2025
c1a672c
Improve split panel header text edit form in dev page
jperals Aug 13, 2025
d94d7a8
Use Input component to fix a11y violations in dev page
jperals Aug 13, 2025
5d05bca
Vertically center actions slot
jperals Aug 13, 2025
886541c
Add expandToViewport propt to ButtonDropdown in dev page
jperals Aug 13, 2025
25a5334
Improve a11y in dev page
jperals Aug 13, 2025
63f7431
Add test coverage for ariaLabel
jperals Aug 13, 2025
05f4019
Merge branch 'main' into feat/split-panel-header-custom
jperals Aug 13, 2025
e60dc6f
Fine tune API doc comments
jperals Aug 13, 2025
c53cf77
Revert unrelated change
jperals Aug 13, 2025
988ce57
Expand headerBefore slot horizontally if it has nothing after it
jperals Aug 13, 2025
612d1be
Remove ariaLabel prop
jperals Aug 14, 2025
8930bdd
Fix cursor on hover
jperals Aug 14, 2025
e6eba58
Improve dev page
jperals Aug 14, 2025
481a554
Adjustments to dev page for easier visual regression testing
jperals Aug 14, 2025
b8f1283
Refactor dev page
jperals Aug 14, 2025
07b1fce
Revert "Remove ariaLabel prop"
jperals Aug 14, 2025
0987d19
Increase size limit
jperals Aug 14, 2025
7d68599
Merge branch 'main' into feat/split-panel-header-custom
jperals Aug 14, 2025
36bafc9
Update API doc comment
jperals Aug 14, 2025
b9901da
Remove duplicated condition in dev page
jperals Aug 14, 2025
de6fe89
Add explicit type definition to the return value of test utils
jperals Aug 15, 2025
47f6b53
Update documenter snapshots
jperals Aug 15, 2025
7d7e175
chore: Add metadata for `inlineLabelText` in multiselect (#3759)
timogasda Aug 15, 2025
d5f2890
chore: Add dnd drag buttons to list (#3652)
gethinwebster Aug 15, 2025
f701ab6
chore: Fix useBaseComponent types (#3742)
just-boris Aug 15, 2025
fbd7975
feat: Make file dropzone design tokens public and themeable (#3753)
avinashbot Aug 15, 2025
68fd5b5
fix: Added custom properties for the Alert focus ring (#3749)
teodoranemes Aug 18, 2025
7f3f494
chore: Adds property filter stories unit tests (#3751)
pan-kot Aug 18, 2025
a1eafcf
fix: Fix skeleton state bugs in app layout widget (#3768)
just-boris Aug 19, 2025
cb88ec2
fix: Fixes tabs focus and navigation when rendered inside iframe (#3769)
pan-kot Aug 19, 2025
966b080
chore: Remove some todo items (#3772)
just-boris Aug 20, 2025
befd15a
Refactor space between main content and description
jperals Aug 19, 2025
8db99f3
Responsiveness fix
jperals Aug 19, 2025
e13fb5b
Make split panel header text optional
jperals Aug 20, 2025
7325210
Do not render header actions when the panel is closed
jperals Aug 20, 2025
4d5cb09
Merge branch 'main' into feat/split-panel-header-custom
jperals Aug 20, 2025
4d786e4
Update Documenter snapshot
jperals Aug 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
{
"path": "lib/components/internal/widget-exports.js",
"brotli": false,
"limit": "830 kB",
"limit": "835 kB",
"ignore": "react-dom"
}
],
Expand Down
324 changes: 324 additions & 0 deletions pages/app-layout/split-panel-with-custom-header.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useContext, useEffect, useRef, useState } from 'react';

import AppLayout, { AppLayoutProps } from '~components/app-layout';
import Badge from '~components/badge';
import Box from '~components/box';
import Button from '~components/button';
import ButtonDropdown from '~components/button-dropdown';
import ColumnLayout from '~components/column-layout';
import FormField from '~components/form-field/internal';
import Header from '~components/header';
import Input from '~components/input';
import FocusLock, { FocusLockRef } from '~components/internal/components/focus-lock';
import Link from '~components/link';
import SpaceBetween from '~components/space-between';
import SplitPanel from '~components/split-panel';

import AppContext, { AppContextType } from '../app/app-context';
import ScreenshotArea from '../utils/screenshot-area';
import { Breadcrumbs, ScrollableDrawerContent, Tools } from './utils/content-blocks';
import labels from './utils/labels';
import { splitPaneli18nStrings } from './utils/strings';
import * as toolsContent from './utils/tools-content';

import styles from './styles.scss';

type SplitPanelDemoContext = React.Context<
AppContextType<{
ariaLabel?: string;
description?: string;
editableHeader: boolean;
headerText?: string;
linkedHeader?: boolean;
renderActionsButtonDropdown: boolean;
renderActionsButtonLink: boolean;
renderBeforeButtons: boolean;
renderBeforeBadge: boolean;
renderInfoLink: boolean;
splitPanelOpen: boolean;
splitPanelPosition: AppLayoutProps.SplitPanelPreferences['position'];
}>
>;

function EditableHeader({ onChange, value }: { onChange: (text: string) => void; value: string }) {
const [internalValue, setInternalValue] = useState(value);
const [editing, setEditing] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const focusLockRef = useRef<FocusLockRef>(null);

useEffect(() => {
if (editing) {
inputRef.current?.focus();
}
}, [editing]);

return editing ? (
<span role="dialog" aria-label="Edit resource name" style={{ display: 'inline-block' }}>
<FocusLock ref={focusLockRef}>
<form
onSubmit={() => {
onChange(internalValue);
setEditing(false);
}}
>
<SpaceBetween direction="horizontal" size="xxs">
<SpaceBetween direction="horizontal" size="s" alignItems="center">
<Box>
<label id="edit-resource-name-label">Resource name</label>
</Box>
<Input
ariaLabelledby="edit-resource-name-label"
value={internalValue}
onChange={({ detail }) => setInternalValue(detail.value)}
ref={inputRef}
/>
</SpaceBetween>
<Button variant="icon" iconName="check" formAction="submit" ariaLabel="Submit" />
<Button
variant="icon"
iconName="close"
formAction="none"
ariaLabel="Submit"
onClick={() => setEditing(false)}
/>
</SpaceBetween>
</form>
</FocusLock>
</span>
) : (
<span className={styles['split-panel-header-margin']}>
<span>{value}</span>{' '}
<Button
variant="inline-icon"
iconName="edit"
ariaLabel="Edit resource name"
onClick={() => setEditing(true)}
></Button>
</span>
);
}

export default function () {
const { urlParams, setUrlParams } = useContext(AppContext as SplitPanelDemoContext);
const [toolsOpen, setToolsOpen] = useState(false);

const {
ariaLabel,
description,
editableHeader,
linkedHeader,
renderActionsButtonDropdown,
renderActionsButtonLink,
renderBeforeBadge,
renderBeforeButtons,
renderInfoLink,
splitPanelOpen,
splitPanelPosition,
} = urlParams;

// Initalize with a known header text for a11y compliance if not provided.
const headerText = urlParams.headerText === undefined ? 'Header text' : urlParams.headerText;

const renderHeaderTextAsLink = !editableHeader && linkedHeader && headerText;
const renderActions = renderActionsButtonDropdown || renderActionsButtonLink;
const renderBefore = editableHeader || linkedHeader || renderBeforeBadge || renderBeforeButtons;
const renderHeaderTextInBeforeSlot = editableHeader || linkedHeader || renderBeforeButtons;

return (
<ScreenshotArea gutters={false} disableAnimations={true}>
<AppLayout
ariaLabels={labels}
breadcrumbs={<Breadcrumbs />}
navigationHide={true}
tools={<Tools>{toolsContent.long}</Tools>}
toolsOpen={toolsOpen}
splitPanelOpen={splitPanelOpen}
onSplitPanelToggle={({ detail }) => setUrlParams({ ...urlParams, splitPanelOpen: detail.open })}
splitPanelPreferences={{
position: splitPanelPosition,
}}
onSplitPanelPreferencesChange={event => {
const { position } = event.detail;
setUrlParams({ splitPanelPosition: position === 'side' ? position : undefined });
}}
onToolsChange={({ detail }) => setToolsOpen(detail.open)}
splitPanel={
<SplitPanel
header={renderHeaderTextInBeforeSlot ? '' : headerText}
i18nStrings={splitPaneli18nStrings}
headerActions={
renderActions && (
<SpaceBetween direction="horizontal" size="xs" alignItems="center">
{renderActionsButtonLink && <Link>Action</Link>}
{renderActionsButtonDropdown && (
<ButtonDropdown
items={[{ id: 'settings', text: 'Settings' }]}
ariaLabel="Control drawer"
variant="icon"
expandToViewport={true}
/>
)}
</SpaceBetween>
)
}
headerBefore={
renderBefore && (
<span
className={
renderBeforeButtons
? styles['split-panel-header-full-width']
: renderHeaderTextAsLink
? styles['split-panel-header-margin']
: undefined
}
>
<span>
{renderBeforeBadge && (
<Box display="inline-block" margin={{ right: renderHeaderTextInBeforeSlot ? 'xs' : 'n' }}>
<Badge>3</Badge>
</Box>
)}
{editableHeader && (
<EditableHeader
value={headerText || ''}
onChange={value => setUrlParams({ ...urlParams, headerText: value })}
/>
)}
{renderHeaderTextAsLink && (
<span className={renderBeforeButtons ? styles['split-panel-header-margin'] : undefined}>
<Link fontSize="inherit" href="#">
{headerText}
</Link>
</span>
)}
{renderHeaderTextInBeforeSlot && !editableHeader && !renderHeaderTextAsLink && (
<span className={styles['split-panel-header-margin']}>{headerText}</span>
)}
</span>
{renderBeforeButtons && (
<SpaceBetween direction="horizontal" size="xs">
<Button>Button</Button> <Button>Button</Button>
</SpaceBetween>
)}
</span>
)
}
headerDescription={description}
headerInfo={
renderInfoLink && (
<Link variant="info" onFollow={() => setToolsOpen(true)}>
Info
</Link>
)
}
ariaLabel={ariaLabel}
>
<ScrollableDrawerContent />
</SplitPanel>
}
content={
<>
<div style={{ marginBottom: '1rem' }}>
<Header variant="h1">Split panel with custom header elements</Header>
</div>
<SpaceBetween size="l">
<ColumnLayout columns={2}>
<FormField label="beforeHeader slot">
<SpaceBetween size="xxs">
<label>
<input
type="checkbox"
checked={renderBeforeBadge}
onChange={({ target }) => setUrlParams({ ...urlParams, renderBeforeBadge: target.checked })}
/>{' '}
Badge
</label>
<label>
<input
type="checkbox"
checked={renderBeforeButtons}
onChange={({ target }) => setUrlParams({ ...urlParams, renderBeforeButtons: target.checked })}
/>{' '}
Buttons
</label>
<label>
<input
type="checkbox"
checked={editableHeader}
onChange={({ target }) => setUrlParams({ ...urlParams, editableHeader: target.checked })}
/>{' '}
Editable header text
</label>
<label>
<input
type="checkbox"
checked={linkedHeader}
disabled={editableHeader}
onChange={({ target }) => setUrlParams({ ...urlParams, linkedHeader: target.checked })}
/>{' '}
Header text as link
</label>
</SpaceBetween>
</FormField>
<FormField label="headerActions slot">
<SpaceBetween size="xxs">
<label>
<input
type="checkbox"
checked={renderActionsButtonDropdown}
onChange={({ target }) =>
setUrlParams({ ...urlParams, renderActionsButtonDropdown: target.checked })
}
/>{' '}
Button dropdown
</label>
<label>
<input
type="checkbox"
checked={renderActionsButtonLink}
onChange={({ target }) =>
setUrlParams({ ...urlParams, renderActionsButtonLink: target.checked })
}
/>{' '}
Inline link button
</label>
</SpaceBetween>
</FormField>
</ColumnLayout>
<FormField label="Header text">
<Input
value={headerText || ''}
onChange={({ detail }) => setUrlParams({ ...urlParams, headerText: detail.value })}
/>
</FormField>
<FormField label="Description">
<Input
value={description || ''}
onChange={({ detail }) => setUrlParams({ ...urlParams, description: detail.value })}
/>
</FormField>
<FormField label="ARIA label">
<Input
value={ariaLabel || ''}
onChange={({ detail }) => setUrlParams({ ...urlParams, ariaLabel: detail.value })}
/>
</FormField>
<FormField>
<label>
<input
type="checkbox"
checked={renderInfoLink}
onChange={({ target }) => setUrlParams({ ...urlParams, renderInfoLink: target.checked })}
/>{' '}
Info link
</label>
</FormField>
</SpaceBetween>
</>
}
/>
</ScreenshotArea>
);
}
14 changes: 14 additions & 0 deletions pages/app-layout/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,17 @@
border-block-start: 2px solid grey;
padding-block: awsui.$space-scaled-s;
}

.split-panel-header-margin {
display: inline-block;
margin-block-start: calc(#{awsui.$space-scaled-xxs} + 1px);
}

.split-panel-header-full-width {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
column-gap: awsui.$space-scaled-xs;
row-gap: awsui.$space-scaled-xxs;
align-items: flex-start;
}
Loading
Loading