diff --git a/packages/react-core/src/demos/Filters/examples/FilterAttributeSearch.tsx b/packages/react-core/src/demos/Filters/examples/FilterAttributeSearch.tsx index 71195ad840e..dfcb3c7ce59 100644 --- a/packages/react-core/src/demos/Filters/examples/FilterAttributeSearch.tsx +++ b/packages/react-core/src/demos/Filters/examples/FilterAttributeSearch.tsx @@ -651,7 +651,7 @@ export const FilterAttributeSearch: React.FunctionComponent = () => { - diff --git a/packages/react-core/src/demos/Filters/examples/FilterCheckboxSelect.tsx b/packages/react-core/src/demos/Filters/examples/FilterCheckboxSelect.tsx index 0fe3cb1c06a..dae722e2173 100644 --- a/packages/react-core/src/demos/Filters/examples/FilterCheckboxSelect.tsx +++ b/packages/react-core/src/demos/Filters/examples/FilterCheckboxSelect.tsx @@ -377,7 +377,7 @@ export const FilterCheckboxSelect: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.threads} {columnNames.apps}
- diff --git a/packages/react-core/src/demos/Filters/examples/FilterFaceted.tsx b/packages/react-core/src/demos/Filters/examples/FilterFaceted.tsx index d2315858746..9accf077041 100644 --- a/packages/react-core/src/demos/Filters/examples/FilterFaceted.tsx +++ b/packages/react-core/src/demos/Filters/examples/FilterFaceted.tsx @@ -467,7 +467,7 @@ export const FilterFaceted: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.threads} {columnNames.apps}
- diff --git a/packages/react-core/src/demos/Filters/examples/FilterMixedSelectGroup.tsx b/packages/react-core/src/demos/Filters/examples/FilterMixedSelectGroup.tsx index e5321c3277c..4991da5ad8b 100644 --- a/packages/react-core/src/demos/Filters/examples/FilterMixedSelectGroup.tsx +++ b/packages/react-core/src/demos/Filters/examples/FilterMixedSelectGroup.tsx @@ -517,7 +517,7 @@ export const FilterMixedSelectGroup: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.threads} {columnNames.apps}
- diff --git a/packages/react-core/src/demos/Filters/examples/FilterSameSelectGroup.tsx b/packages/react-core/src/demos/Filters/examples/FilterSameSelectGroup.tsx index e8c74d36c98..975dfdc5dc9 100644 --- a/packages/react-core/src/demos/Filters/examples/FilterSameSelectGroup.tsx +++ b/packages/react-core/src/demos/Filters/examples/FilterSameSelectGroup.tsx @@ -490,7 +490,7 @@ export const FilterSameSelectGroup: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.threads} {columnNames.apps}
- diff --git a/packages/react-core/src/demos/Filters/examples/FilterSearchInput.tsx b/packages/react-core/src/demos/Filters/examples/FilterSearchInput.tsx index 46111ca27bb..39e352dee9c 100644 --- a/packages/react-core/src/demos/Filters/examples/FilterSearchInput.tsx +++ b/packages/react-core/src/demos/Filters/examples/FilterSearchInput.tsx @@ -298,7 +298,7 @@ export const FilterSearchInput: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.threads} {columnNames.apps}
- diff --git a/packages/react-core/src/demos/Filters/examples/FilterSingleSelect.tsx b/packages/react-core/src/demos/Filters/examples/FilterSingleSelect.tsx index e1ebd4cd9da..f17ec50f154 100644 --- a/packages/react-core/src/demos/Filters/examples/FilterSingleSelect.tsx +++ b/packages/react-core/src/demos/Filters/examples/FilterSingleSelect.tsx @@ -362,7 +362,7 @@ export const FilterSingleSelect: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.threads} {columnNames.apps}
- diff --git a/packages/react-core/src/demos/examples/Card/CardStatus.tsx b/packages/react-core/src/demos/examples/Card/CardStatus.tsx index d74d250af67..05a6ffa0cf2 100644 --- a/packages/react-core/src/demos/examples/Card/CardStatus.tsx +++ b/packages/react-core/src/demos/examples/Card/CardStatus.tsx @@ -94,7 +94,7 @@ export const CardStatus: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.threads} {columnNames.apps}
-
+ {columns.map((column, columnIndex) => ( {column} diff --git a/packages/react-core/src/demos/examples/Tabs/TabsAndTable.tsx b/packages/react-core/src/demos/examples/Tabs/TabsAndTable.tsx index be81a405e79..af9f9a43bd0 100644 --- a/packages/react-core/src/demos/examples/Tabs/TabsAndTable.tsx +++ b/packages/react-core/src/demos/examples/Tabs/TabsAndTable.tsx @@ -230,7 +230,7 @@ export const TablesAndTabs = () => { - diff --git a/packages/react-integration/cypress/integration/tableselectable.spec.ts b/packages/react-integration/cypress/integration/tableselectable.spec.ts index 4d32223c653..7b058348994 100644 --- a/packages/react-integration/cypress/integration/tableselectable.spec.ts +++ b/packages/react-integration/cypress/integration/tableselectable.spec.ts @@ -12,10 +12,7 @@ describe('Table Selectable Test', () => { }); it('Check number of columns', () => { - cy.get('thead').find('th').should('have.length', 5); - - // There should be a canSelectAll input - cy.get('thead').find('td').should('have.length', 1); + cy.get('thead').find('th').should('have.length', 6); }); it('Test selectable checkbox', () => { diff --git a/packages/react-integration/demo-app-ts/src/components/demos/TableDemo/TableComposableDemo.tsx b/packages/react-integration/demo-app-ts/src/components/demos/TableDemo/TableComposableDemo.tsx index 833ee8fa284..8c1e334283f 100644 --- a/packages/react-integration/demo-app-ts/src/components/demos/TableDemo/TableComposableDemo.tsx +++ b/packages/react-integration/demo-app-ts/src/components/demos/TableDemo/TableComposableDemo.tsx @@ -347,7 +347,7 @@ export const TableComposableDemo = () => {
+ {columnNames.name} {columnNames.branches} {columnNames.prs}
- @@ -439,7 +439,7 @@ export const TableComposableDemo = () => { - @@ -616,7 +616,7 @@ export const TableComposableDemo = () => {
+ {columns[0]} {columns[1]} {columns[2]}{columns[2]} {columns[3]} {columns[4]} +
- diff --git a/packages/react-table/src/components/Table/Th.tsx b/packages/react-table/src/components/Table/Th.tsx index aaa07a5bcfe..5729f59c840 100644 --- a/packages/react-table/src/components/Table/Th.tsx +++ b/packages/react-table/src/components/Table/Th.tsx @@ -56,6 +56,14 @@ export interface ThProps stickyRightOffset?: string; /** Indicates the ); + + expect(screen.getByRole('columnheader')).not.toHaveTextContent('Test'); +}); + +test('Renders with screen reader text when screenReaderText is passed in', () => { + render( - - + diff --git a/packages/react-table/src/components/Table/examples/TableActionsOverflow.tsx b/packages/react-table/src/components/Table/examples/TableActionsOverflow.tsx index 5ae2296e33e..e997f7b00d7 100644 --- a/packages/react-table/src/components/Table/examples/TableActionsOverflow.tsx +++ b/packages/react-table/src/components/Table/examples/TableActionsOverflow.tsx @@ -66,7 +66,7 @@ export const TableActions: React.FunctionComponent = () => { - + diff --git a/packages/react-table/src/components/Table/examples/TableCompoundExpandable.tsx b/packages/react-table/src/components/Table/examples/TableCompoundExpandable.tsx index 4f90875d3c5..6e4253e88b7 100644 --- a/packages/react-table/src/components/Table/examples/TableCompoundExpandable.tsx +++ b/packages/react-table/src/components/Table/examples/TableCompoundExpandable.tsx @@ -67,7 +67,7 @@ export const TableCompoundExpandable: React.FunctionComponent = () => { - {repositories.map((repo: Repository, rowIndex: number) => { diff --git a/packages/react-table/src/components/Table/examples/TableDraggable.tsx b/packages/react-table/src/components/Table/examples/TableDraggable.tsx index d37a0307617..7abcf7b7640 100644 --- a/packages/react-table/src/components/Table/examples/TableDraggable.tsx +++ b/packages/react-table/src/components/Table/examples/TableDraggable.tsx @@ -140,10 +140,10 @@ export const TableDraggable: React.FunctionComponent = () => { ]; return ( -
+ {columns[0]} {columns[1]} {columns[2]} is part of a subheader of a nested header */ isSubheader?: boolean; + /** Visually hidden text accessible only via assistive technologies. This must be passed in if the + * th is intended to be visually empty, and must be conveyed as a column header text. + */ + screenReaderText?: string; + /** Provides an accessible name to the th. This should only be passed in when the th contains only non-text + * content, such as a "select all" checkbox or "expand all" toggle. + */ + 'aria-label'?: string; } const ThBase: React.FunctionComponent = ({ @@ -83,8 +91,17 @@ const ThBase: React.FunctionComponent = ({ stickyLeftOffset, stickyRightOffset, isSubheader = false, + screenReaderText, + 'aria-label': ariaLabel, ...props }: ThProps) => { + if (!children && !screenReaderText && !ariaLabel) { + // eslint-disable-next-line no-console + console.warn( + 'Th: Table headers must have an accessible name. If the Th is intended to be visually empty, pass in screenReaderText. If the Th contains only non-text, interactive content such as a checkbox or expand toggle, pass in an aria-label.' + ); + } + const [showTooltip, setShowTooltip] = React.useState(false); const [truncated, setTruncated] = React.useState(false); const cellRef = innerRef ? innerRef : React.createRef(); @@ -188,8 +205,9 @@ const ThBase: React.FunctionComponent = ({ onBlur={() => setShowTooltip(false)} data-label={dataLabel} onMouseEnter={tooltip !== null ? onMouseEnter : onMouseEnterProp} - scope={component === 'th' && children ? scope : null} + scope={component === 'th' ? scope : null} ref={cellRef} + aria-label={ariaLabel} className={css( styles.tableTh, className, @@ -212,7 +230,7 @@ const ThBase: React.FunctionComponent = ({ } as React.CSSProperties })} > - {transformedChildren} + {transformedChildren || (screenReaderText && {screenReaderText})} ); diff --git a/packages/react-table/src/components/Table/__tests__/Th.test.tsx b/packages/react-table/src/components/Table/__tests__/Th.test.tsx new file mode 100644 index 00000000000..ab4f71cf7bc --- /dev/null +++ b/packages/react-table/src/components/Table/__tests__/Th.test.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import { Th } from '../Th'; + +test('Does not render with aria-label by default', () => { + render(); + expect(screen.getByRole('columnheader')).not.toHaveAccessibleName(); +}); + +test('Renders with aria-label when passed in', () => { + render(); + expect(screen.getByRole('columnheader')).toHaveAccessibleName('Test'); +}); + +test('Does not render with screen reader text by default', () => { + render(); + + expect(screen.getByRole('columnheader')).toBeEmptyDOMElement(); +}); + +test('Does not render with screen reader text when children are passed in', () => { + render(Heading label); + + expect(screen.getByRole('columnheader')).toHaveTextContent('Test'); +}); diff --git a/packages/react-table/src/components/Table/examples/TableActions.tsx b/packages/react-table/src/components/Table/examples/TableActions.tsx index 9d42be87717..64ab513762f 100644 --- a/packages/react-table/src/components/Table/examples/TableActions.tsx +++ b/packages/react-table/src/components/Table/examples/TableActions.tsx @@ -116,8 +116,8 @@ export const TableActions: React.FunctionComponent = () => { {columnNames.prs} {columnNames.workspaces} {columnNames.lastCommit} +
{columnNames.prs} {columnNames.workspaces} {columnNames.lastCommit}
{columnNames.prs} {columnNames.workspaces} {columnNames.lastCommit} +
+
- ))} diff --git a/packages/react-table/src/components/Table/examples/TableExpandable.tsx b/packages/react-table/src/components/Table/examples/TableExpandable.tsx index 7cb8ab08e11..67d1291348a 100644 --- a/packages/react-table/src/components/Table/examples/TableExpandable.tsx +++ b/packages/react-table/src/components/Table/examples/TableExpandable.tsx @@ -120,7 +120,7 @@ export const TableExpandable: React.FunctionComponent = () => {
+ {columns.map((column, columnIndex) => ( {column}
- diff --git a/packages/react-table/src/components/Table/examples/TableNestedExpandable.tsx b/packages/react-table/src/components/Table/examples/TableNestedExpandable.tsx index cee6e77f2a1..f2ef7359dcc 100644 --- a/packages/react-table/src/components/Table/examples/TableNestedExpandable.tsx +++ b/packages/react-table/src/components/Table/examples/TableNestedExpandable.tsx @@ -64,7 +64,7 @@ export const TableNestedExpandable: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.branches} {columnNames.prs}
- diff --git a/packages/react-table/src/components/Table/examples/TableNestedStickyHeader.tsx b/packages/react-table/src/components/Table/examples/TableNestedStickyHeader.tsx index 51edc7ec832..fe7b2d90704 100644 --- a/packages/react-table/src/components/Table/examples/TableNestedStickyHeader.tsx +++ b/packages/react-table/src/components/Table/examples/TableNestedStickyHeader.tsx @@ -154,6 +154,9 @@ export const TableNestedHeaders: React.FunctionComponent = () => { {columnNames.destination} + {/* TODO: Remove the following Tr once Core updates towards https://github.com/patternfly/patternfly/issues/6272 + are made for the v5 branch. For v6 branch the row can be removed immediately. + */} diff --git a/packages/react-table/src/components/Table/examples/TableNestedTableExpandable.tsx b/packages/react-table/src/components/Table/examples/TableNestedTableExpandable.tsx index 59855116a9c..37570aa540d 100644 --- a/packages/react-table/src/components/Table/examples/TableNestedTableExpandable.tsx +++ b/packages/react-table/src/components/Table/examples/TableNestedTableExpandable.tsx @@ -131,7 +131,7 @@ export const TableExpandable: React.FunctionComponent = () => {
+ {columnNames.team}
- diff --git a/packages/react-table/src/components/Table/examples/TableSelectableRadio.tsx b/packages/react-table/src/components/Table/examples/TableSelectableRadio.tsx index 710c19655cd..e932fdf30ba 100644 --- a/packages/react-table/src/components/Table/examples/TableSelectableRadio.tsx +++ b/packages/react-table/src/components/Table/examples/TableSelectableRadio.tsx @@ -35,7 +35,7 @@ export const TableSelectableRadio: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.branches} {columnNames.prs}
- diff --git a/packages/react-table/src/components/Table/examples/TableStripedExpandable.tsx b/packages/react-table/src/components/Table/examples/TableStripedExpandable.tsx index ddfd7dee692..e46c7bb61ef 100644 --- a/packages/react-table/src/components/Table/examples/TableStripedExpandable.tsx +++ b/packages/react-table/src/components/Table/examples/TableStripedExpandable.tsx @@ -115,7 +115,7 @@ export const TableStripedExpandable: React.FunctionComponent = () => {
+ {columnNames.name} {columnNames.branches} {columnNames.prs}
- diff --git a/packages/react-table/src/components/Table/utils/decorators/selectable.tsx b/packages/react-table/src/components/Table/utils/decorators/selectable.tsx index b49c947aeeb..71362242176 100644 --- a/packages/react-table/src/components/Table/utils/decorators/selectable.tsx +++ b/packages/react-table/src/components/Table/utils/decorators/selectable.tsx @@ -61,7 +61,7 @@ export const selectable: ITransform = ( return { className: css(styles.tableCheck), - component: 'td', + component: rowId !== -1 ? 'td' : 'th', isVisible: !rowData || !rowData.fullWidth, children: ( {
+ {columnNames.name} {columnNames.branches} {columnNames.prs}
- diff --git a/packages/react-table/src/demos/examples/TableCompoundExpansion.tsx b/packages/react-table/src/demos/examples/TableCompoundExpansion.tsx index 3d3ee1e7977..3a04d2c5bbb 100644 --- a/packages/react-table/src/demos/examples/TableCompoundExpansion.tsx +++ b/packages/react-table/src/demos/examples/TableCompoundExpansion.tsx @@ -51,7 +51,7 @@ export const TableCompoundExpansion: React.FunctionComponent = () => { - @@ -205,7 +205,8 @@ export const TableCompoundExpansion: React.FunctionComponent = () => { - {repositories.map((repo, rowIndex) => { diff --git a/packages/react-table/src/deprecated/components/Table/__tests__/__snapshots__/Table.test.tsx.snap b/packages/react-table/src/deprecated/components/Table/__tests__/__snapshots__/Table.test.tsx.snap index 622d6ed898f..91e79a7ef26 100644 --- a/packages/react-table/src/deprecated/components/Table/__tests__/__snapshots__/Table.test.tsx.snap +++ b/packages/react-table/src/deprecated/components/Table/__tests__/__snapshots__/Table.test.tsx.snap @@ -1366,6 +1366,7 @@ exports[`Table Collapsible nested table 1`] = ` class="pf-v5-c-table__th" data-key="0" data-label="" + scope="" tabindex="-1" /> + +
+ {columns[0]} {columns[1]} {columns[2]}{columnNames.description} {columnNames.date} {columnNames.status} +
{columnNames.prs} {columnNames.workspaces} {columnNames.lastCommit} + +
- - - -