Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/react-aria-components/src/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,11 @@ 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;
}

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/react-aria/src/grid/useGridSelectionCheckbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -26,7 +28,7 @@ export function useGridSelectionCheckbox<T, C extends GridCollection<T>>(
props: AriaGridSelectionCheckboxProps,
state: GridState<T, C>
): GridSelectionCheckboxAria {
let {key} = props;
let {key, id} = props;

let manager = state.selectionManager;
let checkboxId = useId();
Expand All @@ -40,7 +42,7 @@ export function useGridSelectionCheckbox<T, C extends GridCollection<T>>(

return {
checkboxProps: {
id: checkboxId,
id: id ?? checkboxId,
'aria-label': stringFormatter.format('select'),
isSelected,
isDisabled,
Expand Down
9 changes: 7 additions & 2 deletions packages/react-aria/src/table/useTableSelectionCheckbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -43,8 +45,8 @@ export function useTableSelectionCheckbox<T>(
props: AriaTableSelectionCheckboxProps,
state: TableState<T>
): TableSelectionCheckboxAria {
let {key} = props;
const {checkboxProps} = useGridSelectionCheckbox(props, state);
let {key, id} = props;
const {checkboxProps} = useGridSelectionCheckbox({...props, id}, state);

return {
checkboxProps: {
Expand All @@ -64,8 +66,11 @@ export function useTableSelectAllCheckbox<T>(state: TableState<T>): 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:
Expand Down
18 changes: 18 additions & 0 deletions packages/react-aria/test/table/useTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<Table
aria-label="Table with select all id"
selectionMode="multiple"
rowHeaderCustomColumnId="custom-select-all-id">
<TableHeader columns={columns}>
{column => <Column key={column.uid}>{column.name}</Column>}
</TableHeader>
<TableBody items={rows}>
{item => <Row>{columnKey => <Cell>{item[columnKey]}</Cell>}</Row>}
</TableBody>
</Table>
);

expect(container.querySelector('#custom-select-all-id')).toBeTruthy();
});
});
});
3 changes: 2 additions & 1 deletion packages/react-stately/src/table/TableCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface ITableCollection<T> extends IGridCollection<T> {
interface GridCollectionOptions {
showSelectionCheckboxes?: boolean;
showDragButtons?: boolean;
rowHeaderCustomColumnId?: string;
}

const ROW_HEADER_COLUMN_KEY = 'row-header-column-' + Math.random().toString(36).slice(2);
Expand Down Expand Up @@ -218,7 +219,7 @@ export class TableCollection<T> extends GridCollection<T> implements ITableColle
if (opts?.showSelectionCheckboxes) {
let rowHeaderColumn: GridNode<T> = {
type: 'column',
key: ROW_HEADER_COLUMN_KEY,
key: opts?.rowHeaderCustomColumnId || ROW_HEADER_COLUMN_KEY,
value: null,
textValue: '',
level: 0,
Expand Down
18 changes: 14 additions & 4 deletions packages/react-stately/src/table/useTableState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ import {useControlledState} from '../utils/useControlledState';
export interface TableProps<T> extends MultipleSelection, Sortable, Expandable {
/** The elements that make up the table. Includes the TableHeader, TableBody, Columns, and Rows. */
children: [ReactElement<TableHeaderProps<T>>, ReactElement<TableBodyProps<T>>];
/**
* 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<Key>;
/**
Expand Down Expand Up @@ -90,6 +95,7 @@ export interface CollectionBuilderContext<T> {
showDragButtons: boolean;
selectionMode: SelectionMode;
columns: Node<T>[];
rowHeaderCustomColumnId?: string;
}

export interface TableStateProps<T> extends MultipleSelectionStateProps, Expandable, Sortable {
Expand All @@ -107,6 +113,11 @@ export interface TableStateProps<T> 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;
}
Expand All @@ -129,11 +140,10 @@ export function useTableState<T extends object>(props: TableStateProps<T>): 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<T, ITableCollection<T>>(
Expand Down