diff --git a/src/Body/MeasureRow.tsx b/src/Body/MeasureRow.tsx index 264116a2c..aa237c93c 100644 --- a/src/Body/MeasureRow.tsx +++ b/src/Body/MeasureRow.tsx @@ -23,8 +23,24 @@ const MeasureRow: React.FC = ({ const { measureRowRender } = useContext(TableContext, ['measureRowRender']); + // inert also covers a title that sets visibility: visible on itself. It is set on the + // element because React 18 drops a boolean `inert` prop and React 19 reads `inert=""` + // as false. + const setRowRef = React.useCallback((row: HTMLTableRowElement | null) => { + ref.current = row; + row?.setAttribute('inert', ''); + }, []); + + // The row holds copies of the column titles, which may contain controls such as a + // select-all checkbox. aria-hidden alone leaves those focusable; visibility: hidden + // takes them out of the tab order while the row still lays out for measuring. const measureRow = ( - + { if (isVisible(ref.current)) { diff --git a/tests/FixedHeader.spec.jsx b/tests/FixedHeader.spec.jsx index 29add88f3..2137bd218 100644 --- a/tests/FixedHeader.spec.jsx +++ b/tests/FixedHeader.spec.jsx @@ -196,6 +196,48 @@ describe('Table.FixedHeader', () => { expect(container.querySelectorAll('th.rc-table-cell-fix-end')).toHaveLength(1); }); + it('should keep controls copied into the measure row out of the tab order', () => { + const { container } = render( + Select all, dataIndex: 'name', key: 'name' }, + ]} + data={[{ name: 'light', key: 1 }]} + scroll={{ y: 10 }} + />, + ); + + const measureRow = container.querySelector('.rc-table-measure-row'); + expect(measureRow.querySelector('button')).toBeTruthy(); + expect(measureRow.getAttribute('aria-hidden')).toBe('true'); + expect(measureRow.style.visibility).toBe('hidden'); + expect(measureRow.hasAttribute('inert')).toBe(true); + }); + + it('should keep a title that is visible on its own out of the tab order', () => { + const { container } = render( +
+ Select all + + ), + dataIndex: 'name', + key: 'name', + }, + ]} + data={[{ name: 'light', key: 1 }]} + scroll={{ y: 10 }} + />, + ); + + const measureRow = container.querySelector('.rc-table-measure-row'); + expect(measureRow.querySelector('button').style.visibility).toBe('visible'); + expect(measureRow.hasAttribute('inert')).toBe(true); + }); + it('should support measureRowRender to wrap MeasureRow with custom provider', async () => { const FilterDropdown = ({ visible, onVisibleChange }) => (
diff --git a/tests/__snapshots__/ExpandRow.spec.jsx.snap b/tests/__snapshots__/ExpandRow.spec.jsx.snap index e589629f2..72baf12a6 100644 --- a/tests/__snapshots__/ExpandRow.spec.jsx.snap +++ b/tests/__snapshots__/ExpandRow.spec.jsx.snap @@ -157,7 +157,8 @@ exports[`Table.Expand > does not crash if scroll is not set 1`] = `