From b478647beed9be93918c654f52c68ddcb78fb580 Mon Sep 17 00:00:00 2001 From: Jsmitrah Date: Mon, 27 Jul 2026 14:27:23 +0000 Subject: [PATCH 1/5] fix(table): allow specifying an id for 'select all' checkbox --- packages/react-aria-components/src/Table.tsx | 4 ++++ .../src/grid/useGridSelectionCheckbox.ts | 6 ++++-- .../src/table/useTableSelectionCheckbox.ts | 9 +++++++-- .../react-aria/test/table/useTable.test.tsx | 18 ++++++++++++++++++ .../react-stately/src/table/TableCollection.ts | 3 ++- .../react-stately/src/table/useTableState.ts | 14 ++++++++++++-- 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/packages/react-aria-components/src/Table.tsx b/packages/react-aria-components/src/Table.tsx index b4c8c9f3b8c..55c9578aa9b 100644 --- a/packages/react-aria-components/src/Table.tsx +++ b/packages/react-aria-components/src/Table.tsx @@ -615,6 +615,10 @@ export interface TableProps * the Table. */ dragAndDropHooks?: DragAndDropHooks; + /** + * An explicit id to use for the select all checkbox so it remains stable across renders and refreshes. + */ + rowHeaderCustomColumnId?: string; } /** diff --git a/packages/react-aria/src/grid/useGridSelectionCheckbox.ts b/packages/react-aria/src/grid/useGridSelectionCheckbox.ts index 8aa81273213..7f7ab05a017 100644 --- a/packages/react-aria/src/grid/useGridSelectionCheckbox.ts +++ b/packages/react-aria/src/grid/useGridSelectionCheckbox.ts @@ -9,6 +9,8 @@ import {useLocalizedStringFormatter} from '../i18n/useLocalizedStringFormatter'; export interface AriaGridSelectionCheckboxProps { /** A unique key for the checkbox. */ key: Key; + /** An explicit id to use for the checkbox element. */ + id?: string; } export interface GridSelectionCheckboxAria { @@ -26,7 +28,7 @@ export function useGridSelectionCheckbox>( props: AriaGridSelectionCheckboxProps, state: GridState ): GridSelectionCheckboxAria { - let {key} = props; + let {key, id} = props; let manager = state.selectionManager; let checkboxId = useId(); @@ -40,7 +42,7 @@ export function useGridSelectionCheckbox>( return { checkboxProps: { - id: checkboxId, + id: id ?? checkboxId, 'aria-label': stringFormatter.format('select'), isSelected, isDisabled, diff --git a/packages/react-aria/src/table/useTableSelectionCheckbox.ts b/packages/react-aria/src/table/useTableSelectionCheckbox.ts index 0c9805752a5..bd1b8386df9 100644 --- a/packages/react-aria/src/table/useTableSelectionCheckbox.ts +++ b/packages/react-aria/src/table/useTableSelectionCheckbox.ts @@ -21,6 +21,8 @@ import {useLocalizedStringFormatter} from '../i18n/useLocalizedStringFormatter'; export interface AriaTableSelectionCheckboxProps { /** A unique key for the checkbox. */ key: Key; + /** An explicit id to use for the checkbox element. */ + id?: string; } export interface TableSelectionCheckboxAria { @@ -43,8 +45,8 @@ export function useTableSelectionCheckbox( props: AriaTableSelectionCheckboxProps, state: TableState ): TableSelectionCheckboxAria { - let {key} = props; - const {checkboxProps} = useGridSelectionCheckbox(props, state); + let {key, id} = props; + const {checkboxProps} = useGridSelectionCheckbox({...props, id}, state); return { checkboxProps: { @@ -64,8 +66,11 @@ export function useTableSelectAllCheckbox(state: TableState): TableSelectA let {isEmpty, isSelectAll, selectionMode} = state.selectionManager; const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/table'); + let selectionColumn = state.collection.columns.find(column => column.props?.isSelectionCell); + return { checkboxProps: { + id: selectionColumn?.key?.toString(), 'aria-label': stringFormatter.format(selectionMode === 'single' ? 'select' : 'selectAll'), isSelected: isSelectAll, isDisabled: diff --git a/packages/react-aria/test/table/useTable.test.tsx b/packages/react-aria/test/table/useTable.test.tsx index 1211870d45b..67bd0b08718 100644 --- a/packages/react-aria/test/table/useTable.test.tsx +++ b/packages/react-aria/test/table/useTable.test.tsx @@ -157,5 +157,23 @@ describe('useTable', () => { ); expect(getByTestId('test-id').id).toEqual('table-id'); }); + + it('uses a custom id for the select all checkbox', () => { + let {container} = render( + + + {column => {column.name}} + + + {item => {columnKey => {item[columnKey]}}} + +
+ ); + + expect(container.querySelector('#custom-select-all-id')).toBeTruthy(); + }); }); }); diff --git a/packages/react-stately/src/table/TableCollection.ts b/packages/react-stately/src/table/TableCollection.ts index d253f6ff35f..884ab9523e4 100644 --- a/packages/react-stately/src/table/TableCollection.ts +++ b/packages/react-stately/src/table/TableCollection.ts @@ -34,6 +34,7 @@ export interface ITableCollection extends IGridCollection { interface GridCollectionOptions { showSelectionCheckboxes?: boolean; showDragButtons?: boolean; + rowHeaderCustomColumnId?: string; } const ROW_HEADER_COLUMN_KEY = 'row-header-column-' + Math.random().toString(36).slice(2); @@ -218,7 +219,7 @@ export class TableCollection extends GridCollection implements ITableColle if (opts?.showSelectionCheckboxes) { let rowHeaderColumn: GridNode = { type: 'column', - key: ROW_HEADER_COLUMN_KEY, + key: opts?.rowHeaderCustomColumnId || ROW_HEADER_COLUMN_KEY, value: null, textValue: '', level: 0, diff --git a/packages/react-stately/src/table/useTableState.ts b/packages/react-stately/src/table/useTableState.ts index 2bc9c95db02..fc274be0300 100644 --- a/packages/react-stately/src/table/useTableState.ts +++ b/packages/react-stately/src/table/useTableState.ts @@ -33,6 +33,10 @@ import {useControlledState} from '../utils/useControlledState'; export interface TableProps extends MultipleSelection, Sortable, Expandable { /** The elements that make up the table. Includes the TableHeader, TableBody, Columns, and Rows. */ children: [ReactElement>, ReactElement>]; + /** + * An explicit id to use for the select all checkbox so it remains stable across renders and refreshes. + */ + rowHeaderCustomColumnId?: string; /** A list of row keys to disable. */ disabledKeys?: Iterable; /** @@ -90,6 +94,7 @@ export interface CollectionBuilderContext { showDragButtons: boolean; selectionMode: SelectionMode; columns: Node[]; + rowHeaderCustomColumnId?: string; } export interface TableStateProps extends MultipleSelectionStateProps, Expandable, Sortable { @@ -107,6 +112,10 @@ export interface TableStateProps extends MultipleSelectionStateProps, Expanda showDragButtons?: boolean; /** @private - Do not use unless you know what you're doing. */ UNSAFE_selectionState?: MultipleSelectionState; + /** + * An explicit id to use for the select all checkbox so it remains stable across renders and refreshes. + */ + rowHeaderCustomColumnId?: string; /** The id of the column that displays hierarchical data. */ treeColumn?: Key; } @@ -129,11 +138,12 @@ export function useTableState(props: TableStateProps): Tabl showSelectionCheckboxes: showSelectionCheckboxes && selectionMode !== 'none', showDragButtons: showDragButtons, selectionMode, - columns: [] + columns: [], + rowHeaderCustomColumnId: props.rowHeaderCustomColumnId }), // eslint-disable-next-line react-hooks/exhaustive-deps // oxlint-disable-next-line react/react-compiler, react-hooks/exhaustive-deps - [props.children, showSelectionCheckboxes, selectionMode, showDragButtons] + [props.children, showSelectionCheckboxes, selectionMode, showDragButtons, props.rowHeaderCustomColumnId] ); let collection = useCollection>( From a57fa1e5136e2b822cec768e315d4493e26a6f68 Mon Sep 17 00:00:00 2001 From: Jsmitrah Date: Mon, 27 Jul 2026 14:53:04 +0000 Subject: [PATCH 2/5] fixed lint error. --- packages/react-aria-components/src/Table.tsx | 3 ++- packages/react-stately/src/table/useTableState.ts | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/react-aria-components/src/Table.tsx b/packages/react-aria-components/src/Table.tsx index 55c9578aa9b..7dc92b04c5f 100644 --- a/packages/react-aria-components/src/Table.tsx +++ b/packages/react-aria-components/src/Table.tsx @@ -616,7 +616,8 @@ export interface TableProps */ dragAndDropHooks?: DragAndDropHooks; /** - * An explicit id to use for the select all checkbox so it remains stable across renders and refreshes. + * An explicit id to use for the select all checkbox so it remains stable across renders and + * refreshes. */ rowHeaderCustomColumnId?: string; } diff --git a/packages/react-stately/src/table/useTableState.ts b/packages/react-stately/src/table/useTableState.ts index fc274be0300..ef3c2b94107 100644 --- a/packages/react-stately/src/table/useTableState.ts +++ b/packages/react-stately/src/table/useTableState.ts @@ -34,7 +34,8 @@ export interface TableProps extends MultipleSelection, Sortable, Expandable { /** The elements that make up the table. Includes the TableHeader, TableBody, Columns, and Rows. */ children: [ReactElement>, ReactElement>]; /** - * An explicit id to use for the select all checkbox so it remains stable across renders and refreshes. + * An explicit id to use for the select all checkbox so it remains stable across renders and + * refreshes. */ rowHeaderCustomColumnId?: string; /** A list of row keys to disable. */ @@ -113,7 +114,8 @@ export interface TableStateProps extends MultipleSelectionStateProps, Expanda /** @private - Do not use unless you know what you're doing. */ UNSAFE_selectionState?: MultipleSelectionState; /** - * An explicit id to use for the select all checkbox so it remains stable across renders and refreshes. + * An explicit id to use for the select all checkbox so it remains stable across renders and + * refreshes. */ rowHeaderCustomColumnId?: string; /** The id of the column that displays hierarchical data. */ @@ -143,7 +145,13 @@ export function useTableState(props: TableStateProps): Tabl }), // eslint-disable-next-line react-hooks/exhaustive-deps // oxlint-disable-next-line react/react-compiler, react-hooks/exhaustive-deps - [props.children, showSelectionCheckboxes, selectionMode, showDragButtons, props.rowHeaderCustomColumnId] + [ + props.children, + showSelectionCheckboxes, + selectionMode, + showDragButtons, + props.rowHeaderCustomColumnId + ] ); let collection = useCollection>( From 7ef135d7df5b4594418a06f4dbcdb9de7f3200a9 Mon Sep 17 00:00:00 2001 From: Jsmitrah Date: Mon, 27 Jul 2026 15:02:06 +0000 Subject: [PATCH 3/5] Fixed the lint error. --- packages/react-stately/src/table/useTableState.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/react-stately/src/table/useTableState.ts b/packages/react-stately/src/table/useTableState.ts index ef3c2b94107..e6cd28eb727 100644 --- a/packages/react-stately/src/table/useTableState.ts +++ b/packages/react-stately/src/table/useTableState.ts @@ -143,15 +143,7 @@ export function useTableState(props: TableStateProps): Tabl columns: [], rowHeaderCustomColumnId: props.rowHeaderCustomColumnId }), - // eslint-disable-next-line react-hooks/exhaustive-deps - // oxlint-disable-next-line react/react-compiler, react-hooks/exhaustive-deps - [ - props.children, - showSelectionCheckboxes, - selectionMode, - showDragButtons, - props.rowHeaderCustomColumnId - ] + [props.children, showSelectionCheckboxes, selectionMode, showDragButtons, props.rowHeaderCustomColumnId] ); let collection = useCollection>( From ea25b50822c0045d21f0933f9ad2e73b3e226985 Mon Sep 17 00:00:00 2001 From: Jsmitrah Date: Tue, 28 Jul 2026 05:33:00 +0000 Subject: [PATCH 4/5] Fixed the lint error. --- packages/react-stately/src/table/useTableState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-stately/src/table/useTableState.ts b/packages/react-stately/src/table/useTableState.ts index e6cd28eb727..2d5e211f414 100644 --- a/packages/react-stately/src/table/useTableState.ts +++ b/packages/react-stately/src/table/useTableState.ts @@ -143,7 +143,7 @@ export function useTableState(props: TableStateProps): Tabl columns: [], rowHeaderCustomColumnId: props.rowHeaderCustomColumnId }), - [props.children, showSelectionCheckboxes, selectionMode, showDragButtons, props.rowHeaderCustomColumnId] + [showSelectionCheckboxes, selectionMode, showDragButtons, props.rowHeaderCustomColumnId] ); let collection = useCollection>( From 3e4ff442ec59c881f8e3f2f000b758dadb0a7989 Mon Sep 17 00:00:00 2001 From: Jsmitrah Date: Tue, 28 Jul 2026 05:40:41 +0000 Subject: [PATCH 5/5] Fixed the lint error. --- packages/react-stately/src/table/useTableState.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-stately/src/table/useTableState.ts b/packages/react-stately/src/table/useTableState.ts index 2d5e211f414..e6cd28eb727 100644 --- a/packages/react-stately/src/table/useTableState.ts +++ b/packages/react-stately/src/table/useTableState.ts @@ -143,7 +143,7 @@ export function useTableState(props: TableStateProps): Tabl columns: [], rowHeaderCustomColumnId: props.rowHeaderCustomColumnId }), - [showSelectionCheckboxes, selectionMode, showDragButtons, props.rowHeaderCustomColumnId] + [props.children, showSelectionCheckboxes, selectionMode, showDragButtons, props.rowHeaderCustomColumnId] ); let collection = useCollection>(