From 8f9786d0e7a7a8109762b17da863df4bd79e641c Mon Sep 17 00:00:00 2001 From: Zinaida Postica Date: Fri, 28 Aug 2026 17:23:32 +0300 Subject: [PATCH 1/2] feat: add selectAllLabel prop to TableView for customizing the select all checkbox's aria-label --- .../react-spectrum/docs/table/TableView.mdx | 21 ++++++++++ .../react-spectrum/src/table/TableView.tsx | 5 +++ .../src/table/TableViewBase.tsx | 9 +++-- .../stories/table/Table.stories.tsx | 38 +++++++++++++++++++ .../react-spectrum/test/table/TableTests.js | 36 ++++++++++++++++++ packages/@react-aria/table/src/index.ts | 1 + packages/react-aria/exports/useTable.ts | 1 + .../src/table/useTableSelectionCheckbox.ts | 17 ++++++++- 8 files changed, 123 insertions(+), 5 deletions(-) diff --git a/packages/@adobe/react-spectrum/docs/table/TableView.mdx b/packages/@adobe/react-spectrum/docs/table/TableView.mdx index 718d6d52638..9c1e9347307 100644 --- a/packages/@adobe/react-spectrum/docs/table/TableView.mdx +++ b/packages/@adobe/react-spectrum/docs/table/TableView.mdx @@ -301,6 +301,27 @@ The example below applies `isRowHeader` to the "First Name" and "Last Name" colu ``` +When `selectionMode` is `"multiple"`, the select all checkbox in the table header defaults to an accessible label of "Select All". Use the `selectAllLabel` prop to provide a more descriptive label, which is especially useful when a page has multiple TableViews and the default label wouldn't let assistive technology users distinguish between them. + +```tsx example + + + Name + Price + + + + Keyboard + $50 + + + Mouse + $25 + + + +``` + ## Asynchronous loading TableView supports loading data asynchronously, and will display a progress circle reflecting the current load state, set by the `loadingState` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop. diff --git a/packages/@adobe/react-spectrum/src/table/TableView.tsx b/packages/@adobe/react-spectrum/src/table/TableView.tsx index 13bfe9b5c94..7f5c3176adf 100644 --- a/packages/@adobe/react-spectrum/src/table/TableView.tsx +++ b/packages/@adobe/react-spectrum/src/table/TableView.tsx @@ -59,6 +59,11 @@ export interface SpectrumTableProps isQuiet?: boolean; /** Sets what the TableView should render when there is no content to display. */ renderEmptyState?: () => JSX.Element; + /** + * A custom accessibility label for the select all checkbox in the table header. + * If not provided, defaults to the standard localized "Select All" label. + */ + selectAllLabel?: string; /** * Whether `disabledKeys` applies to all interactions, or only selection. * diff --git a/packages/@adobe/react-spectrum/src/table/TableViewBase.tsx b/packages/@adobe/react-spectrum/src/table/TableViewBase.tsx index 1841f41b6d7..acd52518f71 100644 --- a/packages/@adobe/react-spectrum/src/table/TableViewBase.tsx +++ b/packages/@adobe/react-spectrum/src/table/TableViewBase.tsx @@ -161,6 +161,7 @@ export interface TableContextValue { headerMenuOpen: boolean; setHeaderMenuOpen: (val: boolean) => void; renderEmptyState?: () => ReactElement; + selectAllLabel?: string; } export const TableContext = React.createContext | null>(null); @@ -193,6 +194,7 @@ function TableViewBase(props: TableBaseProps, ref: DOMRef(props: TableBaseProps, ref: DOMRef (null); - let {state} = useTableContext(); + let {state, selectAllLabel} = useTableContext(); let isSingleSelectionMode = state.selectionManager.selectionMode === 'single'; let {columnHeaderProps} = useTableColumnHeader( { @@ -1159,7 +1162,7 @@ function TableSelectAllCell({column}) { ref ); - let {checkboxProps} = useTableSelectAllCheckbox(state); + let {checkboxProps} = useTableSelectAllCheckbox(state, {'aria-label': selectAllLabel}); let {hoverProps, isHovered} = useHover({}); return ( diff --git a/packages/@adobe/react-spectrum/stories/table/Table.stories.tsx b/packages/@adobe/react-spectrum/stories/table/Table.stories.tsx index 28afefb4c02..0765855011d 100644 --- a/packages/@adobe/react-spectrum/stories/table/Table.stories.tsx +++ b/packages/@adobe/react-spectrum/stories/table/Table.stories.tsx @@ -920,6 +920,44 @@ export const CustomRowHeaderLabeling: TableStory = { } }; +export const CustomSelectAllLabel: TableStory = { + args: { + 'aria-label': 'Items', + selectionMode: 'multiple', + selectAllLabel: 'Select all items', + width: 500, + height: 200 + }, + render: args => ( + + + Foo + Bar + Baz + + + + One + Two + Three + + + One + Two + Three + + + + ), + name: 'custom select all labeling', + parameters: { + description: { + content: + 'Overrides the accessible label of the select all checkbox in the table header via selectAllLabel.' + } + } +}; + export const CRUD: TableStory = { render: args => , name: 'CRUD' diff --git a/packages/@adobe/react-spectrum/test/table/TableTests.js b/packages/@adobe/react-spectrum/test/table/TableTests.js index c1649a821fa..6e14b842f1c 100644 --- a/packages/@adobe/react-spectrum/test/table/TableTests.js +++ b/packages/@adobe/react-spectrum/test/table/TableTests.js @@ -427,6 +427,42 @@ export let tableTests = () => { expect(cells[5]).toHaveAttribute('aria-colindex', '4'); }); + it('defaults the select all checkbox aria-label to "Select All" when selectAllLabel is not provided', function () { + let {getAllByRole} = render( + + + Foo + + + + Foo 1 + + + + ); + + let checkbox = getAllByRole('checkbox')[0]; + expect(checkbox).toHaveAttribute('aria-label', 'Select All'); + }); + + it('allows the select all checkbox aria-label to be overridden via selectAllLabel', function () { + let {getAllByRole} = render( + + + Foo + + + + Foo 1 + + + + ); + + let checkbox = getAllByRole('checkbox')[0]; + expect(checkbox).toHaveAttribute('aria-label', 'Select all items'); + }); + it('accepts a UNSAFE_className', function () { let {getByRole} = render( ( * @param props - Props for the select all checkbox. * @param state - State of the table, as returned by `useTableState`. */ -export function useTableSelectAllCheckbox(state: TableState): TableSelectAllCheckboxAria { +export function useTableSelectAllCheckbox( + state: TableState, + props: AriaTableSelectAllCheckboxProps = {} +): TableSelectAllCheckboxAria { let {isEmpty, isSelectAll, selectionMode} = state.selectionManager; const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/table'); return { checkboxProps: { - 'aria-label': stringFormatter.format(selectionMode === 'single' ? 'select' : 'selectAll'), + 'aria-label': + props['aria-label'] ?? + stringFormatter.format(selectionMode === 'single' ? 'select' : 'selectAll'), isSelected: isSelectAll, isDisabled: selectionMode !== 'multiple' || From 9692089e71737f76cd03ff2e133e1f7741702488 Mon Sep 17 00:00:00 2001 From: Zinaida Postica Date: Wed, 2 Sep 2026 14:53:14 +0300 Subject: [PATCH 2/2] docs: remove manual selectAllLabel example from TableView.mdx Covered by the auto-generated prop table, per review feedback. --- .../react-spectrum/docs/table/TableView.mdx | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/packages/@adobe/react-spectrum/docs/table/TableView.mdx b/packages/@adobe/react-spectrum/docs/table/TableView.mdx index 9c1e9347307..718d6d52638 100644 --- a/packages/@adobe/react-spectrum/docs/table/TableView.mdx +++ b/packages/@adobe/react-spectrum/docs/table/TableView.mdx @@ -301,27 +301,6 @@ The example below applies `isRowHeader` to the "First Name" and "Last Name" colu ``` -When `selectionMode` is `"multiple"`, the select all checkbox in the table header defaults to an accessible label of "Select All". Use the `selectAllLabel` prop to provide a more descriptive label, which is especially useful when a page has multiple TableViews and the default label wouldn't let assistive technology users distinguish between them. - -```tsx example - - - Name - Price - - - - Keyboard - $50 - - - Mouse - $25 - - - -``` - ## Asynchronous loading TableView supports loading data asynchronously, and will display a progress circle reflecting the current load state, set by the `loadingState` prop. It also supports infinite scrolling to load more data on demand as the user scrolls, via the `onLoadMore` prop.