chore(Dashboard): Improve Table accessibility (#28059)

This commit is contained in:
Geido 2024-04-17 18:24:07 +02:00 committed by GitHub
parent 0e096e8001
commit 69a7bfc88d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 28 additions and 7 deletions

View File

@ -274,7 +274,7 @@ export default typedMemo(function DataTable<D extends object>({
prepareRow(row); prepareRow(row);
const { key: rowKey, ...rowProps } = row.getRowProps(); const { key: rowKey, ...rowProps } = row.getRowProps();
return ( return (
<tr key={rowKey || row.id} {...rowProps}> <tr key={rowKey || row.id} {...rowProps} role="row">
{row.cells.map(cell => {row.cells.map(cell =>
cell.render('Cell', { key: cell.column.id }), cell.render('Cell', { key: cell.column.id }),
)} )}
@ -295,7 +295,11 @@ export default typedMemo(function DataTable<D extends object>({
const { key: footerGroupKey, ...footerGroupProps } = const { key: footerGroupKey, ...footerGroupProps } =
footerGroup.getHeaderGroupProps(); footerGroup.getHeaderGroupProps();
return ( return (
<tr key={footerGroupKey || footerGroup.id} {...footerGroupProps}> <tr
key={footerGroupKey || footerGroup.id}
{...footerGroupProps}
role="row"
>
{footerGroup.headers.map(column => {footerGroup.headers.map(column =>
column.render('Footer', { key: column.id }), column.render('Footer', { key: column.id }),
)} )}

View File

@ -216,6 +216,7 @@ function StickyWrap({
let headerTable: ReactElement | undefined; let headerTable: ReactElement | undefined;
let footerTable: ReactElement | undefined; let footerTable: ReactElement | undefined;
let bodyTable: ReactElement | undefined; let bodyTable: ReactElement | undefined;
if (needSizer) { if (needSizer) {
const theadWithRef = React.cloneElement(thead, { ref: theadRef }); const theadWithRef = React.cloneElement(thead, { ref: theadRef });
const tfootWithRef = tfoot && React.cloneElement(tfoot, { ref: tfootRef }); const tfootWithRef = tfoot && React.cloneElement(tfoot, { ref: tfootRef });
@ -228,8 +229,15 @@ function StickyWrap({
visibility: 'hidden', visibility: 'hidden',
scrollbarGutter: 'stable', scrollbarGutter: 'stable',
}} }}
role="presentation"
> >
{React.cloneElement(table, {}, theadWithRef, tbody, tfootWithRef)} {React.cloneElement(
table,
{ role: 'presentation' },
theadWithRef,
tbody,
tfootWithRef,
)}
</div> </div>
); );
} }
@ -255,9 +263,10 @@ function StickyWrap({
overflow: 'hidden', overflow: 'hidden',
scrollbarGutter: 'stable', scrollbarGutter: 'stable',
}} }}
role="presentation"
> >
{React.cloneElement( {React.cloneElement(
table, React.cloneElement(table, { role: 'presentation' }),
mergeStyleProp(table, fixedTableLayout), mergeStyleProp(table, fixedTableLayout),
colgroup, colgroup,
thead, thead,
@ -274,9 +283,10 @@ function StickyWrap({
overflow: 'hidden', overflow: 'hidden',
scrollbarGutter: 'stable', scrollbarGutter: 'stable',
}} }}
role="presentation"
> >
{React.cloneElement( {React.cloneElement(
table, React.cloneElement(table, { role: 'presentation' }),
mergeStyleProp(table, fixedTableLayout), mergeStyleProp(table, fixedTableLayout),
colgroup, colgroup,
tfoot, tfoot,
@ -303,9 +313,10 @@ function StickyWrap({
scrollbarGutter: 'stable', scrollbarGutter: 'stable',
}} }}
onScroll={sticky.hasHorizontalScroll ? onScroll : undefined} onScroll={sticky.hasHorizontalScroll ? onScroll : undefined}
role="presentation"
> >
{React.cloneElement( {React.cloneElement(
table, React.cloneElement(table, { role: 'presentation' }),
mergeStyleProp(table, fixedTableLayout), mergeStyleProp(table, fixedTableLayout),
colgroup, colgroup,
tbody, tbody,
@ -321,6 +332,7 @@ function StickyWrap({
height: sticky.realHeight || maxHeight, height: sticky.realHeight || maxHeight,
overflow: 'hidden', overflow: 'hidden',
}} }}
role="table"
> >
{headerTable} {headerTable}
{bodyTable} {bodyTable}

View File

@ -519,6 +519,8 @@ export default function TableChart<D extends DataRecord = DataRecord>(
`; `;
const cellProps = { const cellProps = {
'aria-labelledby': `header-${column.key}`,
role: 'cell',
// show raw number in title in case of numeric values // show raw number in title in case of numeric values
title: typeof value === 'number' ? String(value) : undefined, title: typeof value === 'number' ? String(value) : undefined,
onClick: onClick:
@ -547,6 +549,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
value == null ? 'dt-is-null' : '', value == null ? 'dt-is-null' : '',
isActiveFilterValue(key, value) ? ' dt-is-active-filter' : '', isActiveFilterValue(key, value) ? ' dt-is-active-filter' : '',
].join(' '), ].join(' '),
tabIndex: 0,
}; };
if (html) { if (html) {
if (truncateLongCells) { if (truncateLongCells) {
@ -576,6 +579,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
value && value < 0 ? 'negative' : 'positive', value && value < 0 ? 'negative' : 'positive',
)} )}
css={cellBarStyles} css={cellBarStyles}
role="presentation"
/> />
)} )}
{truncateLongCells ? ( {truncateLongCells ? (
@ -593,13 +597,13 @@ export default function TableChart<D extends DataRecord = DataRecord>(
}, },
Header: ({ column: col, onClick, style, onDragStart, onDrop }) => ( Header: ({ column: col, onClick, style, onDragStart, onDrop }) => (
<th <th
id={`header-${column.key}`}
title={t('Shift + Click to sort by multiple columns')} title={t('Shift + Click to sort by multiple columns')}
className={[className, col.isSorted ? 'is-sorted' : ''].join(' ')} className={[className, col.isSorted ? 'is-sorted' : ''].join(' ')}
style={{ style={{
...sharedStyle, ...sharedStyle,
...style, ...style,
}} }}
tabIndex={0}
onKeyDown={(e: React.KeyboardEvent<HTMLElement>) => { onKeyDown={(e: React.KeyboardEvent<HTMLElement>) => {
// programatically sort column on keypress // programatically sort column on keypress
if (Object.values(ACTION_KEYS).includes(e.key)) { if (Object.values(ACTION_KEYS).includes(e.key)) {
@ -615,6 +619,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
onDragEnter: e => e.preventDefault(), onDragEnter: e => e.preventDefault(),
onDrop, onDrop,
})} })}
tabIndex={0}
> >
{/* can't use `columnWidth &&` because it may also be zero */} {/* can't use `columnWidth &&` because it may also be zero */}
{config.columnWidth ? ( {config.columnWidth ? (