diff --git a/CHANGELOG.md b/CHANGELOG.md index 4439faa251f..a441c63d1c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Fixed logo icons with static SVG IDs causing accessibility errors when multiples of the same logo were present ([#5204](https://github.com/elastic/eui/pull/5204)) - Fixed several `EuiDataGrid` console errors that occur on column drag/drop reorder ([#5209](https://github.com/elastic/eui/pull/5209)) +- Fixed an accessibility issue where `EuiDataGrid` cells weren't owned by `role=row` elements ([#5213](https://github.com/elastic/eui/pull/5213)) **Reverts** diff --git a/scripts/a11y-testing.js b/scripts/a11y-testing.js index daaf22b469f..d66613d043d 100644 --- a/scripts/a11y-testing.js +++ b/scripts/a11y-testing.js @@ -13,15 +13,6 @@ const docsPages = async (root, page) => { `${root}#/forms/color-selection`, `${root}#/forms/date-picker`, `${root}#/forms/super-date-picker`, - `${root}#/tabular-content/data-grid`, - `${root}#/tabular-content/data-grid-in-memory-settings`, - `${root}#/tabular-content/data-grid-schemas-and-popovers`, - `${root}#/tabular-content/data-grid-focus`, - `${root}#/tabular-content/data-grid-styling-and-control`, - `${root}#/tabular-content/data-grid-control-columns`, - `${root}#/tabular-content/data-grid-footer-row`, - `${root}#/tabular-content/data-grid-virtualization`, - `${root}#/tabular-content/data-grid-row-heights-options`, ]; return [ diff --git a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index 27962a9b854..023f30801ad 100644 --- a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -1039,6 +1039,11 @@ Array [
+
+
+
+
+
{ const requiredProps = { + gridId: 'grid', isFullScreen: false, headerIsInteractive: true, rowCount: 1, diff --git a/src/components/datagrid/body/data_grid_body.tsx b/src/components/datagrid/body/data_grid_body.tsx index 6627c842c5e..ed9ff544744 100644 --- a/src/components/datagrid/body/data_grid_body.tsx +++ b/src/components/datagrid/body/data_grid_body.tsx @@ -10,6 +10,7 @@ import classNames from 'classnames'; import React, { forwardRef, FunctionComponent, + ReactElement, useCallback, useContext, useEffect, @@ -72,6 +73,7 @@ export const Cell: FunctionComponent = ({ rowHeightsOptions, getRowHeight, rowHeightUtils, + gridId, } = data; const { headerRowHeight } = useContext(DataGridWrapperRowsContext); @@ -119,12 +121,15 @@ export const Cell: FunctionComponent = ({ [`euiDataGridRowCell--${textTransform}`]: textTransform, }); + const cellId = `datagrid-${gridId}-cell-${rowIndex},${columnIndex}`; + if (isLeadingControlColumn) { const leadingColumn = leadingControlColumns[columnIndex]; const { id, rowCellRender } = leadingColumn; cellContent = ( = ({ cellContent = ( = ({ cellContent = ( = ( rowHeightUtils, virtualizationOptions, gridStyles, + gridId, } = props; const [headerRowRef, setHeaderRowRef] = useState(null); @@ -673,6 +681,57 @@ export const EuiDataGridBody: FunctionComponent = ( finalWidth = window.innerWidth; } + const [ + { + renderedRowsStartIndex, + renderedRowsStopIndex, + renderedColumnsStartIndex, + renderedColumnsStopIndex, + }, + setRenderedRowIndices, + ] = useState({ + renderedRowsStartIndex: -1, + renderedRowsStopIndex: -1, + renderedColumnsStartIndex: -1, + renderedColumnsStopIndex: -1, + }); + + const onItemsRendered = useCallback< + Required['onItemsRendered'] + >( + ({ + overscanRowStartIndex, + overscanRowStopIndex, + overscanColumnStartIndex, + overscanColumnStopIndex, + }) => { + setRenderedRowIndices({ + renderedRowsStartIndex: overscanRowStartIndex, + renderedRowsStopIndex: overscanRowStopIndex, + renderedColumnsStartIndex: overscanColumnStartIndex, + renderedColumnsStopIndex: overscanColumnStopIndex, + }); + }, + [setRenderedRowIndices] + ); + + const accessibilityRows: ReactElement[] = []; + for (let i = renderedRowsStartIndex; i <= renderedRowsStopIndex; i++) { + const rowId = `datagrid-${gridId}-row-${i}}`; + const ownedCells: string[] = []; + for ( + let j = renderedColumnsStartIndex; + j <= renderedColumnsStopIndex; + j++ + ) { + ownedCells.push(`datagrid-${gridId}-cell-${i},${j}`); + } + + accessibilityRows.push( +
+ ); + } + return ( = ( mutationRef(el); }} > + {accessibilityRows} {(IS_JEST_ENVIRONMENT || finalWidth > 0) && ( = ( interactiveCellId, rowHeightsOptions, rowHeightUtils, + gridId, }} rowCount={ IS_JEST_ENVIRONMENT || headerRowHeight > 0 diff --git a/src/components/datagrid/body/data_grid_cell.test.tsx b/src/components/datagrid/body/data_grid_cell.test.tsx index 7b8e9592469..fbf0c7e10c8 100644 --- a/src/components/datagrid/body/data_grid_cell.test.tsx +++ b/src/components/datagrid/body/data_grid_cell.test.tsx @@ -15,6 +15,7 @@ import { EuiDataGridCell } from './data_grid_cell'; describe('EuiDataGridCell', () => { const requiredProps = { + id: 'cell', rowIndex: 0, visibleRowIndex: 0, colIndex: 0, diff --git a/src/components/datagrid/body/data_grid_cell.tsx b/src/components/datagrid/body/data_grid_cell.tsx index f5eca7ff69e..51c390d1a4d 100644 --- a/src/components/datagrid/body/data_grid_cell.tsx +++ b/src/components/datagrid/body/data_grid_cell.tsx @@ -396,6 +396,7 @@ export class EuiDataGridCell extends Component< render() { const { + id, width, isExpandable, popoverContent: PopoverContent, @@ -598,6 +599,7 @@ export class EuiDataGridCell extends Component< } ref={this.setCellRef} {...cellProps} + id={id} data-test-subj="dataGridRowCell" onKeyDown={handleCellKeyDown} onFocus={this.onFocus} diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 18966b6046d..c3a19002221 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -535,6 +535,7 @@ describe('EuiDataGrid', () => { Object { "className": "euiDataGridRowCell euiDataGridRowCell--firstColumn customClass", "data-test-subj": "dataGridRowCell", + "id": "datagrid-generated-id-cell-0,0", "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], @@ -554,6 +555,7 @@ describe('EuiDataGrid', () => { Object { "className": "euiDataGridRowCell euiDataGridRowCell--lastColumn customClass", "data-test-subj": "dataGridRowCell", + "id": "datagrid-generated-id-cell-0,1", "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], @@ -573,6 +575,7 @@ describe('EuiDataGrid', () => { Object { "className": "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn customClass", "data-test-subj": "dataGridRowCell", + "id": "datagrid-generated-id-cell-1,0", "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], @@ -592,6 +595,7 @@ describe('EuiDataGrid', () => { Object { "className": "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn customClass", "data-test-subj": "dataGridRowCell", + "id": "datagrid-generated-id-cell-1,1", "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index b41984e7079..52da534f8cf 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -883,6 +883,7 @@ export const EuiDataGrid: FunctionComponent = (props) => { rowHeightUtils={rowHeightUtils} virtualizationOptions={virtualizationOptions || {}} gridStyles={gridStyles} + gridId={gridId} />
diff --git a/src/components/datagrid/data_grid_types.ts b/src/components/datagrid/data_grid_types.ts index 3ab5c00d620..137b09969cb 100644 --- a/src/components/datagrid/data_grid_types.ts +++ b/src/components/datagrid/data_grid_types.ts @@ -332,6 +332,7 @@ export interface EuiDataGridBodyProps { rowHeightsOptions?: EuiDataGridRowHeightsOptions; rowHeightUtils: RowHeightUtils; gridStyles?: EuiDataGridStyle; + gridId: string; } export interface EuiDataGridCellValueElementProps { /** @@ -364,6 +365,10 @@ export interface EuiDataGridCellValueElementProps { } export interface EuiDataGridCellProps { + /** + * required when used outside of a role="row" element, so a row can own this cell + */ + id?: string; rowIndex: number; visibleRowIndex: number; colIndex: number; @@ -396,7 +401,7 @@ export interface EuiDataGridCellState { export type EuiDataGridCellValueProps = Omit< EuiDataGridCellProps, - 'width' | 'interactiveCellId' | 'popoverContent' + 'id' | 'width' | 'interactiveCellId' | 'popoverContent' >; export interface EuiDataGridControlColumn { /**