From 8a19c4efdc435a70a5b4619757d365af627df533 Mon Sep 17 00:00:00 2001 From: kostasdano Date: Tue, 14 May 2024 13:49:00 +0300 Subject: [PATCH] feat: enhance keyboard navigation --- .../Table/Table-ColumnAndRowSizing.snap | 10 +++++++--- .../Table/Table-ColumnChooser.snap | 10 +++++++--- .../Updated Components/Table/Table-Sorting.snap | 10 +++++++--- src/components/Table/components/TH/TH.style.ts | 12 ++++++++++-- .../TH/components/THOptions/THOptions.tsx | 15 +++++++++++---- .../components/ColumnChooser/ColumnChooser.tsx | 14 +++++++++++--- 6 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/__storyshots__/Updated Components/Table/Table-ColumnAndRowSizing.snap b/src/__storyshots__/Updated Components/Table/Table-ColumnAndRowSizing.snap index 28200913e..9a743bb73 100644 --- a/src/__storyshots__/Updated Components/Table/Table-ColumnAndRowSizing.snap +++ b/src/__storyshots__/Updated Components/Table/Table-ColumnAndRowSizing.snap @@ -41,12 +41,16 @@ letter-spacing: 0.015625rem; } -.emotion-3 [data-header-role='options'] { +.emotion-3 [data-header-role='options'] button { opacity: 0; } -.emotion-3:hover [data-header-role='options'], -.emotion-3:focus-visible [data-header-role='options'] { +.emotion-3:hover [data-header-role='options'] button, +.emotion-3:focus-visible [data-header-role='options'] button { + opacity: 1; +} + +.emotion-3 button:focus-visible { opacity: 1; } diff --git a/src/__storyshots__/Updated Components/Table/Table-ColumnChooser.snap b/src/__storyshots__/Updated Components/Table/Table-ColumnChooser.snap index cded5e979..d37bff86b 100644 --- a/src/__storyshots__/Updated Components/Table/Table-ColumnChooser.snap +++ b/src/__storyshots__/Updated Components/Table/Table-ColumnChooser.snap @@ -141,12 +141,16 @@ letter-spacing: 0.015625rem; } -.emotion-9 [data-header-role='options'] { +.emotion-9 [data-header-role='options'] button { opacity: 0; } -.emotion-9:hover [data-header-role='options'], -.emotion-9:focus-visible [data-header-role='options'] { +.emotion-9:hover [data-header-role='options'] button, +.emotion-9:focus-visible [data-header-role='options'] button { + opacity: 1; +} + +.emotion-9 button:focus-visible { opacity: 1; } diff --git a/src/__storyshots__/Updated Components/Table/Table-Sorting.snap b/src/__storyshots__/Updated Components/Table/Table-Sorting.snap index 3a9367f09..b2e38c89f 100644 --- a/src/__storyshots__/Updated Components/Table/Table-Sorting.snap +++ b/src/__storyshots__/Updated Components/Table/Table-Sorting.snap @@ -141,12 +141,16 @@ letter-spacing: 0.015625rem; } -.emotion-9 [data-header-role='options'] { +.emotion-9 [data-header-role='options'] button { opacity: 0; } -.emotion-9:hover [data-header-role='options'], -.emotion-9:focus-visible [data-header-role='options'] { +.emotion-9:hover [data-header-role='options'] button, +.emotion-9:focus-visible [data-header-role='options'] button { + opacity: 1; +} + +.emotion-9 button:focus-visible { opacity: 1; } diff --git a/src/components/Table/components/TH/TH.style.ts b/src/components/Table/components/TH/TH.style.ts index 21e36db52..1451189db 100644 --- a/src/components/Table/components/TH/TH.style.ts +++ b/src/components/Table/components/TH/TH.style.ts @@ -25,15 +25,23 @@ export const thContainer = ${generateStylesFromTokens(theme.tokens.typography.get('normal.body02'))}; [data-header-role='options'] { - opacity: ${hasVisibleOptions ? 1 : 0}; + button { + opacity: ${hasVisibleOptions ? 1 : 0}; + } } &:hover, &:focus-visible { [data-header-role='options'] { - opacity: 1; + button { + opacity: 1; + } } } + + button:focus-visible { + opacity: 1; + } `; }; diff --git a/src/components/Table/components/TH/components/THOptions/THOptions.tsx b/src/components/Table/components/TH/components/THOptions/THOptions.tsx index 2a1dceff9..ff369a24c 100644 --- a/src/components/Table/components/TH/components/THOptions/THOptions.tsx +++ b/src/components/Table/components/TH/components/THOptions/THOptions.tsx @@ -17,16 +17,17 @@ type Props = { }; const THOptions: React.FC = ({ isMultiSortable, onSort, onButtonClick }) => { - const [isBtnOpen, setBtnOpen] = React.useState(false); + const [isBtnOpen, setIsBtnOpen] = React.useState(false); const btnRef = useRef(null); + const listRef = useRef(null); const handleBtnClick = (e) => { if (e?.preventDefault) { e?.preventDefault(); } - setBtnOpen((isOpen) => { + setIsBtnOpen((isOpen) => { onButtonClick(!isOpen); return !isOpen; @@ -42,7 +43,7 @@ const THOptions: React.FC = ({ isMultiSortable, onSort, onButtonClick }) if (key === 'sortAscending' || key === 'sortDescending') { onButtonClick(false); - setBtnOpen(false); + setIsBtnOpen(false); } }; @@ -58,13 +59,18 @@ const THOptions: React.FC = ({ isMultiSortable, onSort, onButtonClick }) aria-expanded={isBtnOpen ? 'true' : undefined} type="tertiary" size="compact" + onKeyDown={(e) => { + if (e.key === 'ArrowDown') { + listRef.current.focus(); + } + }} /> { - setBtnOpen((isOpen) => { + setIsBtnOpen((isOpen) => { onButtonClick(!isOpen); return !isOpen; @@ -74,6 +80,7 @@ const THOptions: React.FC = ({ isMultiSortable, onSort, onButtonClick }) crossOffset={72} > , 'columns' | 'columnsConfig'>; /** @TODO create a generic Popover component */ const ColumnChooser: React.FC = ({ columns, columnsConfig }) => { - const [isBtnOpen, setBtnOpen] = React.useState(false); + const [isBtnOpen, setIsBtnOpen] = React.useState(false); const options = flattenColumns(columns); + const menuRef = useRef(null); + const [selectedKeys, setSelectedKeys] = React.useState( new Set( Object.keys(columnsConfig.columnVisibility).filter( @@ -39,7 +41,7 @@ const ColumnChooser: React.FC = ({ columns, columnsConfig }) => { e?.preventDefault(); } - setBtnOpen((isOpen) => !isOpen); + setIsBtnOpen((isOpen) => !isOpen); }; const handleSelectionChange = (keys) => { @@ -66,6 +68,11 @@ const ColumnChooser: React.FC = ({ columns, columnsConfig }) => { aria-expanded={isBtnOpen ? 'true' : undefined} iconLeftName="columnChooser" type="secondary" + onKeyDown={(e) => { + if (e.key === 'ArrowDown') { + menuRef.current.focus(); + } + }} > Edit Columns @@ -73,10 +80,11 @@ const ColumnChooser: React.FC = ({ columns, columnsConfig }) => { triggerRef={btnRef} css={popoverStyle} isOpen={isBtnOpen} - onOpenChange={() => setBtnOpen((isOpen) => !isOpen)} + onOpenChange={() => setIsBtnOpen((isOpen) => !isOpen)} shouldCloseOnInteractOutside={() => true} >