Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Table grid nav api #1480

Closed
wants to merge 57 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
33c0b32
update table-role helper APIs with isWidget
pan-kot Aug 23, 2023
7d76975
additional test for widget focusability
pan-kot Aug 23, 2023
4a544f0
Make navigation work when first row has index other than 1
pan-kot Aug 23, 2023
0f6ea43
Fix integ test
pan-kot Aug 23, 2023
566d370
API and test page
pan-kot Aug 24, 2023
6ee80fa
Disable existing navigation for role=grid
pan-kot Aug 24, 2023
add512b
use grid navigation
pan-kot Aug 24, 2023
4619a58
Fix and enhance tests
pan-kot Aug 24, 2023
238f251
Implement isWidget behavior
pan-kot Aug 24, 2023
e17c0a5
add widget styles
pan-kot Aug 25, 2023
75f3a93
wip: dispatch focus to header cells'
pan-kot Aug 25, 2023
205986b
Merge branch 'main' of github.com:cloudscape-design/components into t…
pan-kot Aug 28, 2023
2827eb6
fix selection cell focus
pan-kot Aug 28, 2023
9668c16
adjust resize handle styles
pan-kot Aug 28, 2023
350ff82
Fix header widget condition
pan-kot Aug 28, 2023
9c5f113
Merge branch 'main' of github.com:cloudscape-design/components into t…
pan-kot Aug 28, 2023
750b43d
fix th focus for non-sticky header
pan-kot Aug 28, 2023
ca6157d
fix a11y for selection control
pan-kot Aug 28, 2023
61342d7
fix scroll into view
pan-kot Aug 28, 2023
4bdd569
simplify test page
pan-kot Aug 28, 2023
b42e661
update docs
pan-kot Aug 28, 2023
1736847
fix unit tests
pan-kot Aug 28, 2023
93806d6
Fix integ test
pan-kot Aug 28, 2023
342b783
Keep table wrapper scrollable
pan-kot Aug 28, 2023
06963ef
Fix navigation for multi-element cells
pan-kot Aug 28, 2023
6dab5ec
Make entered editable cell a widget
pan-kot Aug 28, 2023
ad2e102
WIP: dialog cell contept
pan-kot Aug 29, 2023
00eef9f
Merge branch 'main' of github.com:cloudscape-design/components into t…
pan-kot Sep 6, 2023
6e171dc
TODO commends and remove isDialog API
pan-kot Sep 6, 2023
15bd574
wip: update api
pan-kot Sep 6, 2023
e1e289d
custom control
pan-kot Sep 6, 2023
4c7c805
fix focus for conditional element
pan-kot Sep 6, 2023
50f4218
Merge branch 'main' of github.com:cloudscape-design/components into t…
pan-kot Oct 13, 2023
d347580
remove merge artifact
pan-kot Oct 13, 2023
9bf1fa9
resolve merge issues
pan-kot Oct 13, 2023
047fce1
Merge branch 'main' of github.com:cloudscape-design/components into t…
pan-kot Nov 2, 2023
2b13c1b
remove dedicated grid nav page
pan-kot Nov 2, 2023
fb2d9ff
fix first cell focus juggle
pan-kot Nov 2, 2023
84d0f80
fix accidental element focus unmuting
pan-kot Nov 2, 2023
8399d07
edge focus move fix
pan-kot Nov 2, 2023
1a49cc0
add keyboard navigation option to pages
pan-kot Nov 2, 2023
7a41682
suppressKeyboardNavigationFor API
pan-kot Nov 2, 2023
b94a466
code cleanup
pan-kot Nov 2, 2023
7f795ff
revert changes to inline editor
pan-kot Nov 2, 2023
a4e7903
simpler fix for reassigning tabIndex=-1
pan-kot Nov 3, 2023
1e88d4e
Merge branch 'main' of github.com:cloudscape-design/components into t…
pan-kot Nov 8, 2023
38bd897
remove obsolete changes
pan-kot Nov 8, 2023
40f8eee
remove obsolete changes
pan-kot Nov 8, 2023
3d2e929
fix focus for editable cell
pan-kot Nov 8, 2023
e98350e
remove obsolete test
pan-kot Nov 8, 2023
f99ad91
fix user focusout
pan-kot Nov 14, 2023
912abbc
fix outline for body cells
pan-kot Nov 14, 2023
4cc64a0
fix page
pan-kot Nov 14, 2023
f08a895
remove suppression api
pan-kot Nov 14, 2023
06d4f7c
fix grid nav inline edit exit
pan-kot Nov 14, 2023
a8d1cb9
reuse outline styles for header cells
pan-kot Nov 14, 2023
0b819b2
update api prop name and description
pan-kot Nov 16, 2023
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
6 changes: 6 additions & 0 deletions pages/table-fragments/grid-navigation-custom.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,12 @@ function EditableStateCell({ value, onChange }: { value: InstanceState; onChange
const [active, setActive] = useState(false);
const dialogRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (active) {
dialogRef.current?.querySelector('input')?.focus();
}
}, [active]);

if (!active) {
return value === 'TERMINATED' ? (
<StatusIndicator {...stateToStatusIndicator[value]} />
Expand Down
93 changes: 59 additions & 34 deletions pages/table/editable.page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { ForwardedRef, forwardRef, useEffect, useRef, useState } from 'react';
import React, { ForwardedRef, forwardRef, useContext, useEffect, useRef, useState } from 'react';
import Header from '~components/header';
import Input from '~components/input';
import Alert from '~components/alert';
Expand All @@ -9,9 +9,16 @@ import Select, { SelectProps } from '~components/select';
import TimeInput, { TimeInputProps } from '~components/time-input';
import Autosuggest, { AutosuggestProps } from '~components/autosuggest';
import Multiselect, { MultiselectProps } from '~components/multiselect';
import { Link, Box, Button, Modal, SpaceBetween } from '~components';
import { Link, Box, Button, Modal, SpaceBetween, Checkbox } from '~components';
import { initialItems, DistributionInfo, tlsVersions, originSuggestions, tagOptions } from './editable-data';
import ScreenshotArea from '../utils/screenshot-area';
import AppContext, { AppContextType } from '../app/app-context';

type PageContext = React.Context<
AppContextType<{
keyboardNavigation: boolean;
}>
>;

let __editStateDirty = false;

Expand Down Expand Up @@ -201,7 +208,10 @@ let errorsMeta = new WeakMap<DistributionInfo, string>();

const Demo = forwardRef(
(
{ setModalVisible }: { setModalVisible: React.Dispatch<React.SetStateAction<boolean>> },
{
setModalVisible,
keyboardNavigation,
}: { setModalVisible: React.Dispatch<React.SetStateAction<boolean>>; keyboardNavigation: boolean },
tableRef: ForwardedRef<TableProps.Ref>
) => {
const [items, setItems] = useState(initialItems);
Expand Down Expand Up @@ -253,12 +263,16 @@ const Demo = forwardRef(
resizableColumns={true}
ariaLabels={ariaLabels}
stickyHeader={true}
enableKeyboardNavigation={keyboardNavigation}
/>
);
}
);

export default function () {
const { urlParams, setUrlParams } = useContext(AppContext as PageContext);
const keyboardNavigation = urlParams.keyboardNavigation ?? false;

const [modalVisible, setModalVisible] = useState(false);
const tableRef = useRef<TableProps.Ref>(null);

Expand All @@ -275,37 +289,48 @@ export default function () {
});

return (
<ScreenshotArea disableAnimations={true}>
<input data-testid="focus" aria-label="focus input" />
<Demo setModalVisible={setModalVisible} ref={tableRef} />
<Modal
visible={modalVisible}
header="Discard changes"
closeAriaLabel="Close modal"
onDismiss={withCleanState(() => setModalVisible(false))}
footer={
<Box float="right">
<SpaceBetween direction="horizontal" size="xs">
<Button variant="link" onClick={withCleanState(() => setModalVisible(false))}>
Cancel
</Button>
<Button
variant="primary"
onClick={withCleanState(() => {
setModalVisible(false);
tableRef.current?.cancelEdit?.();
})}
>
Discard
</Button>
</SpaceBetween>
</Box>
}
<>
<Checkbox
checked={keyboardNavigation}
onChange={event => {
setUrlParams({ keyboardNavigation: event.detail.checked });
window.location.reload();
}}
>
<Alert type="warning" statusIconAriaLabel="Warning">
Are you sure you want to discard any unsaved changes?
</Alert>
</Modal>
</ScreenshotArea>
Keyboard navigation
</Checkbox>
<ScreenshotArea disableAnimations={true}>
<input data-testid="focus" aria-label="focus input" />
<Demo setModalVisible={setModalVisible} ref={tableRef} keyboardNavigation={keyboardNavigation} />
<Modal
visible={modalVisible}
header="Discard changes"
closeAriaLabel="Close modal"
onDismiss={withCleanState(() => setModalVisible(false))}
footer={
<Box float="right">
<SpaceBetween direction="horizontal" size="xs">
<Button variant="link" onClick={withCleanState(() => setModalVisible(false))}>
Cancel
</Button>
<Button
variant="primary"
onClick={withCleanState(() => {
setModalVisible(false);
tableRef.current?.cancelEdit?.();
})}
>
Discard
</Button>
</SpaceBetween>
</Box>
}
>
<Alert type="warning" statusIconAriaLabel="Warning">
Are you sure you want to discard any unsaved changes?
</Alert>
</Modal>
</ScreenshotArea>
</>
);
}
143 changes: 85 additions & 58 deletions pages/table/inline-actions.page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import React, { useContext } from 'react';
import Box from '~components/box';
import Button from '~components/button';
import ButtonDropdown from '~components/button-dropdown';
Expand All @@ -11,6 +11,14 @@ import ScreenshotArea from '../utils/screenshot-area';
import { Instance, generateItems } from './generate-data';
import { columnsConfig, selectionLabels } from './shared-configs';
import Link from '~components/link';
import AppContext, { AppContextType } from '../app/app-context';
import { Checkbox } from '~components';

type PageContext = React.Context<
AppContextType<{
keyboardNavigation: boolean;
}>
>;

const items = generateItems(10);

Expand Down Expand Up @@ -148,63 +156,82 @@ const columnDefinitionsOnlyIcons: TableProps.ColumnDefinition<Instance>[] = [
];

export default function () {
const { urlParams, setUrlParams } = useContext(AppContext as PageContext);
const keyboardNavigation = urlParams.keyboardNavigation ?? false;

return (
<ScreenshotArea style={{ padding: '10px 50px' }}>
<Box padding="l">
<h1>Tables with inline actions</h1>
<SpaceBetween size="l">
<Table
ariaLabels={selectionLabels}
header={<Header>Table with single actions</Header>}
columnDefinitions={columnDefinitionsSingle}
items={items}
/>
<Table
ariaLabels={selectionLabels}
header={<Header>Table with multiple actions</Header>}
columnDefinitions={columnDefinitionsMultiple}
items={items}
/>
<Table
ariaLabels={selectionLabels}
header={
<Header
actions={
<SpaceBetween size="xs" direction="horizontal">
<ButtonDropdown
items={[
{ id: 'connect', text: 'Connect' },
{ id: 'view', text: 'View details' },
{ id: 'manage', text: 'Manage instances' },
]}
>
Actions
</ButtonDropdown>
<Button variant="primary">Launch instance</Button>
</SpaceBetween>
}
>
Table with action dropdowns
</Header>
}
selectionType="multi"
columnDefinitions={columnDefinitionsDropdown}
items={items}
/>
<Table
ariaLabels={selectionLabels}
header={<Header>Table with mixed actions</Header>}
columnDefinitions={columnDefinitionsMixed}
items={items}
/>
<Table
ariaLabels={selectionLabels}
header={<Header>Table with only icon actions</Header>}
columnDefinitions={columnDefinitionsOnlyIcons}
items={items}
/>
</SpaceBetween>
</Box>
</ScreenshotArea>
<>
<Checkbox
checked={keyboardNavigation}
onChange={e => {
setUrlParams({ keyboardNavigation: e.detail.checked });
window.location.reload();
}}
>
Keyboard navigation
</Checkbox>
<ScreenshotArea style={{ padding: '10px 50px' }}>
<Box padding="l">
<h1>Tables with inline actions</h1>
<SpaceBetween size="l">
<Table
ariaLabels={selectionLabels}
header={<Header>Table with single actions</Header>}
columnDefinitions={columnDefinitionsSingle}
items={items}
enableKeyboardNavigation={keyboardNavigation}
/>
<Table
ariaLabels={selectionLabels}
header={<Header>Table with multiple actions</Header>}
columnDefinitions={columnDefinitionsMultiple}
items={items}
enableKeyboardNavigation={keyboardNavigation}
/>
<Table
ariaLabels={selectionLabels}
header={
<Header
actions={
<SpaceBetween size="xs" direction="horizontal">
<ButtonDropdown
items={[
{ id: 'connect', text: 'Connect' },
{ id: 'view', text: 'View details' },
{ id: 'manage', text: 'Manage instances' },
]}
>
Actions
</ButtonDropdown>
<Button variant="primary">Launch instance</Button>
</SpaceBetween>
}
>
Table with action dropdowns
</Header>
}
selectionType="multi"
columnDefinitions={columnDefinitionsDropdown}
items={items}
enableKeyboardNavigation={keyboardNavigation}
/>
<Table
ariaLabels={selectionLabels}
header={<Header>Table with mixed actions</Header>}
columnDefinitions={columnDefinitionsMixed}
items={items}
enableKeyboardNavigation={keyboardNavigation}
/>
<Table
ariaLabels={selectionLabels}
header={<Header>Table with only icon actions</Header>}
columnDefinitions={columnDefinitionsOnlyIcons}
items={items}
enableKeyboardNavigation={keyboardNavigation}
/>
</SpaceBetween>
</Box>
</ScreenshotArea>
</>
);
}
12 changes: 12 additions & 0 deletions pages/table/resizable-columns.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type PageContext = React.Context<
stickyHeader: boolean;
resizableColumns: boolean;
fullPage: boolean;
keyboardNavigation: boolean;
}>
>;

Expand All @@ -105,6 +106,7 @@ export default function App() {
const wrapLines = urlParams.wrapLines ?? false;
const stickyHeader = urlParams.stickyHeader ?? false;
const resizableColumns = urlParams.resizableColumns ?? true;
const keyboardNavigation = urlParams.keyboardNavigation ?? false;
const fullPage = urlParams.fullPage ?? false;

const [renderKey, setRenderKey] = useState(0);
Expand Down Expand Up @@ -161,6 +163,15 @@ export default function App() {
<Checkbox checked={fullPage} onChange={event => setUrlParams({ fullPage: event.detail.checked })}>
Full page table
</Checkbox>
<Checkbox
checked={keyboardNavigation}
onChange={event => {
setUrlParams({ keyboardNavigation: event.detail.checked });
window.location.reload();
}}
>
Keyboard navigation
</Checkbox>
</div>
<div>
{columnsConfig.map(column => (
Expand Down Expand Up @@ -200,6 +211,7 @@ export default function App() {
onSortingChange={event => setSorting(event.detail)}
onColumnWidthsChange={handleWidthChange}
variant={fullPage ? 'full-page' : undefined}
enableKeyboardNavigation={keyboardNavigation}
/>
</ScreenshotArea>
</SpaceBetween>
Expand Down
11 changes: 11 additions & 0 deletions pages/table/sticky-columns.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type DemoContext = React.Context<
resizableColumns: boolean;
stickyHeader: boolean;
sortingDisabled: boolean;
enableKeyboardNavigation: boolean;
selectionType: undefined | 'single' | 'multi';
stickyColumnsFirst: string;
stickyColumnsLast: string;
Expand Down Expand Up @@ -190,6 +191,16 @@ export default () => {
>
Sorting disabled
</Checkbox>

<Checkbox
checked={urlParams.enableKeyboardNavigation}
onChange={event => {
setUrlParams({ enableKeyboardNavigation: event.detail.checked });
window.location.reload();
}}
>
Keyboard navigation
</Checkbox>
</FormField>

<FormField label="Selection type">
Expand Down
7 changes: 7 additions & 0 deletions src/__tests__/__snapshots__/documenter.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12953,6 +12953,13 @@ Use it in conjunction with the content display preference of the [collection pre
"optional": true,
"type": "string",
},
Object {
"description": "Use this property to activate advanced keyboard navigation and focusing behaviors.
When set to \`true\` table cells become navigable with arrow keys and the entire table has a single Tab stop.",
"name": "enableKeyboardNavigation",
"optional": true,
"type": "boolean",
},
Object {
"description": " Use this property to inform screen readers which range of items is currently displayed in the table.
It specifies the index (1-based) of the first item in the table.
Expand Down
Loading
Loading