From b1a5f23359206ee7aa5733611ef2f69c4207b19e Mon Sep 17 00:00:00 2001 From: jsmitrah Date: Mon, 27 Jul 2026 16:19:24 +0530 Subject: [PATCH 1/4] implemented the table column header focus. --- .../react-spectrum/src/table/TableView.tsx | 7 ++ .../react-spectrum/test/table/TableTests.js | 83 ++++++++++++++++++- packages/react-aria-components/src/Table.tsx | 7 ++ .../react-aria-components/test/Table.test.js | 22 +++++ packages/react-aria/src/grid/useGrid.ts | 10 ++- .../src/selection/useSelectableCollection.ts | 22 ++++- .../src/table/TableKeyboardDelegate.ts | 31 ++++++- packages/react-aria/src/table/useTable.ts | 15 +++- 8 files changed, 187 insertions(+), 10 deletions(-) diff --git a/packages/@adobe/react-spectrum/src/table/TableView.tsx b/packages/@adobe/react-spectrum/src/table/TableView.tsx index 13bfe9b5c94..666d1411879 100644 --- a/packages/@adobe/react-spectrum/src/table/TableView.tsx +++ b/packages/@adobe/react-spectrum/src/table/TableView.tsx @@ -67,6 +67,13 @@ export interface SpectrumTableProps disabledBehavior?: DisabledBehavior; /** Handler that is called when a user performs an action on a row. */ onAction?: (key: Key) => void; + /** + * Whether the first row or the first column header should be focused when the user tabs into the + * table. + * + * @default 'row' + */ + initialFocus?: 'row' | 'columnheader'; /** * Handler that is called when a user starts a column resize. */ diff --git a/packages/@adobe/react-spectrum/test/table/TableTests.js b/packages/@adobe/react-spectrum/test/table/TableTests.js index 25e502c6bdd..f8d400d5455 100644 --- a/packages/@adobe/react-spectrum/test/table/TableTests.js +++ b/packages/@adobe/react-spectrum/test/table/TableTests.js @@ -1400,6 +1400,20 @@ export let tableTests = () => { moveFocus('Home'); expect(document.activeElement).toBe(tree.getAllByRole('row')[1]); }); + + it('should still focus the first cell in a row with Home when initialFocus="columnheader"', function () { + let tree = renderTable('en-US', {initialFocus: 'columnheader'}); + focusCell(tree, 'Bar 1'); + moveFocus('Home'); + expect(document.activeElement).toBe(getCell(tree, 'Foo 1')); + }); + + it('should still focus the first cell in the first row with ctrl + Home when initialFocus="columnheader"', function () { + let tree = renderTable('en-US', {initialFocus: 'columnheader'}); + focusCell(tree, 'Bar 2'); + moveFocus('Home', {ctrlKey: true}); + expect(document.activeElement).toBe(getCell(tree, 'Foo 1')); + }); }); describe('End', function () { @@ -1671,11 +1685,11 @@ export let tableTests = () => { }); describe('focus marshalling', function () { - let renderFocusable = () => + let renderFocusable = (props = {}) => render( <> - + Foo Bar @@ -1816,6 +1830,71 @@ export let tableTests = () => { expect(document.activeElement).toBe(within(table).getAllByRole('row')[1]); }); + it('should move focus to the first column header when tabbing into the table from the start with initialFocus="columnheader"', function () { + let tree = renderFocusable({initialFocus: 'columnheader', selectionMode: 'none'}); + + let table = tree.getByRole('grid'); + expect(table).toHaveAttribute('tabIndex', '0'); + + let before = tree.getByTestId('before'); + act(() => before.focus()); + + fireEvent.keyDown(before, {key: 'Tab'}); + act(() => { + within(table).getAllByRole('switch')[0].focus(); + }); + fireEvent.keyUp(before, {key: 'Tab'}); + + expect(document.activeElement).toBe(within(table).getAllByRole('columnheader')[0]); + }); + + it('should move focus to the first column header when tabbing into the table with initialFocus="columnheader" even if a row is already selected', function () { + let tree = render( + <> + + + + Foo + Bar + baz + + + + Foo 1 + Bar 1 + Baz 1 + + + Foo 2 + Bar 2 + Baz 2 + + + + + + ); + + let table = tree.getByRole('grid'); + expect(within(table).getAllByRole('row')[1]).toHaveAttribute('aria-selected', 'true'); + + let before = tree.getByTestId('before'); + act(() => before.focus()); + + fireEvent.keyDown(before, {key: 'Tab'}); + act(() => { + table.focus(); + }); + fireEvent.keyUp(before, {key: 'Tab'}); + + expect(document.activeElement).toBe(within(table).getAllByRole('columnheader')[0]); + }); + it('should move focus to the last row when tabbing into the table from the end', function () { let tree = renderFocusable(); diff --git a/packages/react-aria-components/src/Table.tsx b/packages/react-aria-components/src/Table.tsx index 4b1b4092e47..f8ece092a8d 100644 --- a/packages/react-aria-components/src/Table.tsx +++ b/packages/react-aria-components/src/Table.tsx @@ -614,6 +614,13 @@ export interface TableProps * the Table. */ dragAndDropHooks?: DragAndDropHooks; + /** + * Whether the first row or the first column header should be focused when the user tabs into the + * table. + * + * @default 'row' + */ + initialFocus?: 'row' | 'columnheader'; } /** diff --git a/packages/react-aria-components/test/Table.test.js b/packages/react-aria-components/test/Table.test.js index 1f83529ca71..ce4d3bcba9e 100644 --- a/packages/react-aria-components/test/Table.test.js +++ b/packages/react-aria-components/test/Table.test.js @@ -732,6 +732,28 @@ describe('Table', () => { expect(column).toHaveClass('focus'); }); + it('should focus the first column header when tabbing in with initialFocus="columnheader"', async () => { + let {getAllByRole} = renderTable({tableProps: {initialFocus: 'columnheader'}}); + + await user.tab(); + expect(document.activeElement).toBe(getAllByRole('columnheader')[0]); + }); + + it('should focus the first column header when tabbing in with initialFocus="columnheader" even if a row is already selected', async () => { + let {getAllByRole} = renderTable({ + tableProps: { + initialFocus: 'columnheader', + selectionMode: 'single', + defaultSelectedKeys: ['1'] + } + }); + + expect(getAllByRole('row')[1]).toHaveAttribute('aria-selected', 'true'); + + await user.tab(); + expect(document.activeElement).toBe(getAllByRole('columnheader')[0]); + }); + it('should support press state', async () => { let {getAllByRole} = renderTable({ tableProps: {selectionMode: 'multiple'}, diff --git a/packages/react-aria/src/grid/useGrid.ts b/packages/react-aria/src/grid/useGrid.ts index b9897552a7f..09b84cd5d13 100644 --- a/packages/react-aria/src/grid/useGrid.ts +++ b/packages/react-aria/src/grid/useGrid.ts @@ -54,6 +54,12 @@ export interface GridProps extends DOMProps, AriaLabelingProps { * @default 'row' */ focusMode?: 'row' | 'cell'; + /** + * Which item in the grid should be focused when the user tabs into it for the first time. + * + * @default 'row' + */ + initialFocus?: 'row' | 'columnheader'; /** * A function that returns the text that should be announced by assistive technology when a row is * added or removed from selection. @@ -108,6 +114,7 @@ export function useGrid( disallowTypeAhead, keyboardDelegate, focusMode, + initialFocus, scrollRef, getRowText, onRowAction, @@ -157,7 +164,8 @@ export function useGrid( isVirtualized, scrollRef, disallowTypeAhead, - escapeKeyBehavior + escapeKeyBehavior, + UNSTABLE_ignoreSelectionOnEntry: initialFocus === 'columnheader' }); let id = useId(props.id); diff --git a/packages/react-aria/src/selection/useSelectableCollection.ts b/packages/react-aria/src/selection/useSelectableCollection.ts index a8599907985..be3ec972b40 100644 --- a/packages/react-aria/src/selection/useSelectableCollection.ts +++ b/packages/react-aria/src/selection/useSelectableCollection.ts @@ -133,6 +133,13 @@ export interface AriaSelectableCollectionOptions { * @private */ UNSTABLE_focusOnEntry?: 'first' | 'last'; + /** + * Whether the delegate's first/last key should be used instead of the current selection when the + * collection receives focus for the first time (i.e. before the user has interacted with it). + * + * @private + */ + UNSTABLE_ignoreSelectionOnEntry?: boolean; } export interface SelectableCollectionAria { @@ -162,7 +169,8 @@ export function useSelectableCollection( // If no scrollRef is provided, assume the collection ref is the scrollable region scrollRef = ref, linkBehavior = 'action', - UNSTABLE_focusOnEntry + UNSTABLE_focusOnEntry, + UNSTABLE_ignoreSelectionOnEntry = false } = options; let {direction} = useLocale(); let router = useRouter(); @@ -448,9 +456,17 @@ export function useSelectableCollection( relatedTarget && e.currentTarget.compareDocumentPosition(relatedTarget) & Node.DOCUMENT_POSITION_FOLLOWING ) { - navigateToKey(manager.lastSelectedKey ?? delegate.getLastKey?.()); + navigateToKey( + UNSTABLE_ignoreSelectionOnEntry + ? delegate.getLastKey?.() + : (manager.lastSelectedKey ?? delegate.getLastKey?.()) + ); } else { - navigateToKey(manager.firstSelectedKey ?? delegate.getFirstKey?.()); + navigateToKey( + UNSTABLE_ignoreSelectionOnEntry + ? delegate.getFirstKey?.() + : (manager.firstSelectedKey ?? delegate.getFirstKey?.()) + ); } } else if (scrollRef.current) { // Restore the scroll position to what it was before. diff --git a/packages/react-aria/src/table/TableKeyboardDelegate.ts b/packages/react-aria/src/table/TableKeyboardDelegate.ts index a85724650f9..fd04aa87448 100644 --- a/packages/react-aria/src/table/TableKeyboardDelegate.ts +++ b/packages/react-aria/src/table/TableKeyboardDelegate.ts @@ -11,15 +11,44 @@ */ import {getChildNodes, getFirstItem} from 'react-stately/private/collections/getChildNodes'; -import {GridKeyboardDelegate} from '../grid/GridKeyboardDelegate'; +import {GridKeyboardDelegate, GridKeyboardDelegateOptions} from '../grid/GridKeyboardDelegate'; import {ITableCollection} from 'react-stately/private/table/TableCollection'; import {Key, Node} from '@react-types/shared'; +export interface TableKeyboardDelegateOptions extends GridKeyboardDelegateOptions< + ITableCollection +> { + /** + * Whether the first row or the first column header should be focused when the user tabs into the + * table. + * + * @default 'row' + */ + initialFocus?: 'row' | 'columnheader'; +} + export class TableKeyboardDelegate extends GridKeyboardDelegate> { + private initialFocus: 'row' | 'columnheader'; + + constructor(options: TableKeyboardDelegateOptions) { + super(options); + this.initialFocus = options.initialFocus ?? 'row'; + } + protected isCell(node: Node): boolean { return node.type === 'cell' || node.type === 'rowheader' || node.type === 'column'; } + getFirstKey(fromKey?: Key, global?: boolean): Key | null { + if (fromKey == null && this.initialFocus === 'columnheader') { + let firstColumn = this.collection.columns[0]; + if (firstColumn) { + return firstColumn.key; + } + } + return super.getFirstKey(fromKey, global); + } + getKeyBelow(key: Key, options?: {includeDisabled?: boolean}): Key | null { let startItem = this.collection.getItem(key); if (!startItem) { diff --git a/packages/react-aria/src/table/useTable.ts b/packages/react-aria/src/table/useTable.ts index 581e5d8a351..d0cb15db946 100644 --- a/packages/react-aria/src/table/useTable.ts +++ b/packages/react-aria/src/table/useTable.ts @@ -36,6 +36,13 @@ export interface AriaTableProps extends GridProps { layoutDelegate?: LayoutDelegate; /** @deprecated - Use layoutDelegate instead. */ layout?: DeprecatedLayout; + /** + * Whether the first row or the first column header should be focused when the user tabs into the + * table. + * + * @default 'row' + */ + initialFocus?: 'row' | 'columnheader'; } interface DeprecatedLayout { @@ -66,7 +73,7 @@ export function useTable( state: TableState | TreeGridState, ref: RefObject ): GridAria { - let {keyboardDelegate, isVirtualized, layoutDelegate, layout} = props; + let {keyboardDelegate, isVirtualized, layoutDelegate, layout, initialFocus} = props; // By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down). // When virtualized, the layout object will be passed in as a prop and override this. @@ -84,7 +91,8 @@ export function useTable( direction, collator, layoutDelegate, - layout + layout, + initialFocus }), [ keyboardDelegate, @@ -95,7 +103,8 @@ export function useTable( direction, collator, layoutDelegate, - layout + layout, + initialFocus ] ); let id = useId(props.id); From cc36fd3b51538ccde1033ce6191f6868271c9b25 Mon Sep 17 00:00:00 2001 From: jsmitrah Date: Wed, 29 Jul 2026 17:43:30 +0530 Subject: [PATCH 2/4] addressed review comments on Table initialFocus --- .../react-spectrum/test/table/TableTests.js | 46 +++++++----- .../stories/Table.stories.tsx | 73 +++++++++++++++++++ .../react-aria-components/test/Table.test.js | 7 +- packages/react-aria/src/grid/useGrid.ts | 10 +-- .../src/selection/useSelectableCollection.ts | 22 +----- .../src/table/TableKeyboardDelegate.ts | 4 +- 6 files changed, 110 insertions(+), 52 deletions(-) diff --git a/packages/@adobe/react-spectrum/test/table/TableTests.js b/packages/@adobe/react-spectrum/test/table/TableTests.js index f9bd2425306..30a1672875e 100644 --- a/packages/@adobe/react-spectrum/test/table/TableTests.js +++ b/packages/@adobe/react-spectrum/test/table/TableTests.js @@ -1830,25 +1830,35 @@ export let tableTests = () => { expect(document.activeElement).toBe(within(table).getAllByRole('row')[1]); }); - it('should move focus to the first column header when tabbing into the table from the start with initialFocus="columnheader"', function () { + it('should move focus to the first column header when tabbing into the table from the start with initialFocus="columnheader"', async function () { let tree = renderFocusable({initialFocus: 'columnheader', selectionMode: 'none'}); let table = tree.getByRole('grid'); expect(table).toHaveAttribute('tabIndex', '0'); - let before = tree.getByTestId('before'); - act(() => before.focus()); - - fireEvent.keyDown(before, {key: 'Tab'}); - act(() => { - within(table).getAllByRole('switch')[0].focus(); - }); - fireEvent.keyUp(before, {key: 'Tab'}); + await user.tab(); + expect(document.activeElement).toBe(tree.getByTestId('before')); + await user.tab(); expect(document.activeElement).toBe(within(table).getAllByRole('columnheader')[0]); }); - it('should move focus to the first column header when tabbing into the table with initialFocus="columnheader" even if a row is already selected', function () { + it('should focus the first real column header, not the selection checkbox column, when tabbing in with initialFocus="columnheader"', async function () { + let tree = renderFocusable({initialFocus: 'columnheader', selectionMode: 'multiple'}); + + let table = tree.getByRole('grid'); + let columnHeaders = within(table).getAllByRole('columnheader'); + // The auto-generated selection checkbox column is inserted before the "Foo" column. + expect(within(columnHeaders[0]).queryByRole('checkbox')).not.toBeNull(); + + await user.tab(); + await user.tab(); + + expect(document.activeElement).toBe(columnHeaders[1]); + expect(document.activeElement).toHaveTextContent('Foo'); + }); + + it('should focus the selected row rather than the first column header when tabbing into the table with initialFocus="columnheader" if a row is already selected', async function () { let tree = render( <> @@ -1881,18 +1891,14 @@ export let tableTests = () => { ); let table = tree.getByRole('grid'); - expect(within(table).getAllByRole('row')[1]).toHaveAttribute('aria-selected', 'true'); - - let before = tree.getByTestId('before'); - act(() => before.focus()); + let selectedRow = within(table).getAllByRole('row')[1]; + expect(selectedRow).toHaveAttribute('aria-selected', 'true'); - fireEvent.keyDown(before, {key: 'Tab'}); - act(() => { - table.focus(); - }); - fireEvent.keyUp(before, {key: 'Tab'}); + await user.tab(); + expect(document.activeElement).toBe(tree.getByTestId('before')); - expect(document.activeElement).toBe(within(table).getAllByRole('columnheader')[0]); + await user.tab(); + expect(document.activeElement).toBe(selectedRow); }); it('should move focus to the last row when tabbing into the table from the end', function () { diff --git a/packages/react-aria-components/stories/Table.stories.tsx b/packages/react-aria-components/stories/Table.stories.tsx index 3e41436acf9..85ce3b39cf9 100644 --- a/packages/react-aria-components/stories/Table.stories.tsx +++ b/packages/react-aria-components/stories/Table.stories.tsx @@ -988,6 +988,79 @@ export const OnLoadMoreTableStory: StoryObj = { } }; +const InitialFocusExample = (args: { + initialFocus?: 'row' | 'columnheader'; + selectionMode?: 'none' | 'single' | 'multiple'; +}) => ( +
+ + + {args.selectionMode !== 'none' && ( + + + + )} + + Name + + Type + Date Modified + + + + {args.selectionMode !== 'none' && ( + + + + )} + Games + File folder + 6/7/2020 + + + {args.selectionMode !== 'none' && ( + + + + )} + Program Files + File folder + 4/7/2021 + + + {args.selectionMode !== 'none' && ( + + + + )} + bootmgr + System file + 11/20/2010 + + +
+
+); + +export const InitialFocusExampleStory: StoryObj = { + render: InitialFocusExample, + name: 'initialFocus="columnheader" with selection checkbox column', + args: { + initialFocus: 'columnheader', + selectionMode: 'multiple' + }, + argTypes: { + initialFocus: { + control: 'radio', + options: ['row', 'columnheader'] + }, + selectionMode: { + control: 'radio', + options: ['none', 'single', 'multiple'] + } + } +}; + export const VirtualizedTable: TableStory = () => { let items: {id: number; foo: string; bar: string; baz: string}[] = []; for (let i = 0; i < 1000; i++) { diff --git a/packages/react-aria-components/test/Table.test.js b/packages/react-aria-components/test/Table.test.js index 724b670693f..3214bcfca08 100644 --- a/packages/react-aria-components/test/Table.test.js +++ b/packages/react-aria-components/test/Table.test.js @@ -868,7 +868,7 @@ describe('Table', () => { expect(document.activeElement).toBe(getAllByRole('columnheader')[0]); }); - it('should focus the first column header when tabbing in with initialFocus="columnheader" even if a row is already selected', async () => { + it('should focus the selected row rather than the first column header when tabbing in with initialFocus="columnheader" if a row is already selected', async () => { let {getAllByRole} = renderTable({ tableProps: { initialFocus: 'columnheader', @@ -877,10 +877,11 @@ describe('Table', () => { } }); - expect(getAllByRole('row')[1]).toHaveAttribute('aria-selected', 'true'); + let selectedRow = getAllByRole('row')[1]; + expect(selectedRow).toHaveAttribute('aria-selected', 'true'); await user.tab(); - expect(document.activeElement).toBe(getAllByRole('columnheader')[0]); + expect(document.activeElement).toBe(selectedRow); }); it('should support press state', async () => { diff --git a/packages/react-aria/src/grid/useGrid.ts b/packages/react-aria/src/grid/useGrid.ts index d83f9e3725f..88244b23e98 100644 --- a/packages/react-aria/src/grid/useGrid.ts +++ b/packages/react-aria/src/grid/useGrid.ts @@ -54,12 +54,6 @@ export interface GridProps extends DOMProps, AriaLabelingProps { * @default 'row' */ focusMode?: 'row' | 'cell'; - /** - * Which item in the grid should be focused when the user tabs into it for the first time. - * - * @default 'row' - */ - initialFocus?: 'row' | 'columnheader'; /** * A function that returns the text that should be announced by assistive technology when a row is * added or removed from selection. @@ -121,7 +115,6 @@ export function useGrid( disallowTypeAhead, keyboardDelegate, focusMode, - initialFocus, scrollRef, getRowText, onRowAction, @@ -172,8 +165,7 @@ export function useGrid( isVirtualized, scrollRef, disallowTypeAhead, - escapeKeyBehavior, - UNSTABLE_ignoreSelectionOnEntry: initialFocus === 'columnheader' + escapeKeyBehavior }); let id = useId(props.id); diff --git a/packages/react-aria/src/selection/useSelectableCollection.ts b/packages/react-aria/src/selection/useSelectableCollection.ts index a4b27cbdb88..6cb2e4c7772 100644 --- a/packages/react-aria/src/selection/useSelectableCollection.ts +++ b/packages/react-aria/src/selection/useSelectableCollection.ts @@ -136,13 +136,6 @@ export interface AriaSelectableCollectionOptions { * @private */ UNSTABLE_focusOnEntry?: 'first' | 'last'; - /** - * Whether the delegate's first/last key should be used instead of the current selection when the - * collection receives focus for the first time (i.e. before the user has interacted with it). - * - * @private - */ - UNSTABLE_ignoreSelectionOnEntry?: boolean; } export interface SelectableCollectionAria { @@ -172,8 +165,7 @@ export function useSelectableCollection( // If no scrollRef is provided, assume the collection ref is the scrollable region scrollRef = ref, linkBehavior = 'action', - UNSTABLE_focusOnEntry, - UNSTABLE_ignoreSelectionOnEntry = false + UNSTABLE_focusOnEntry } = options; let {direction} = useLocale(); let router = useRouter(); @@ -499,17 +491,9 @@ export function useSelectableCollection( relatedTarget && e.currentTarget.compareDocumentPosition(relatedTarget) & Node.DOCUMENT_POSITION_FOLLOWING ) { - navigateToKey( - UNSTABLE_ignoreSelectionOnEntry - ? delegate.getLastKey?.() - : (manager.lastSelectedKey ?? delegate.getLastKey?.()) - ); + navigateToKey(manager.lastSelectedKey ?? delegate.getLastKey?.()); } else { - navigateToKey( - UNSTABLE_ignoreSelectionOnEntry - ? delegate.getFirstKey?.() - : (manager.firstSelectedKey ?? delegate.getFirstKey?.()) - ); + navigateToKey(manager.firstSelectedKey ?? delegate.getFirstKey?.()); } } else if (scrollRef.current) { // Restore the scroll position to what it was before. diff --git a/packages/react-aria/src/table/TableKeyboardDelegate.ts b/packages/react-aria/src/table/TableKeyboardDelegate.ts index fd04aa87448..4cb7b2f9db8 100644 --- a/packages/react-aria/src/table/TableKeyboardDelegate.ts +++ b/packages/react-aria/src/table/TableKeyboardDelegate.ts @@ -41,7 +41,9 @@ export class TableKeyboardDelegate extends GridKeyboardDelegate !column.props?.isDragButtonCell && !column.props?.isSelectionCell + ); if (firstColumn) { return firstColumn.key; } From 2c3c2255bb456449d3da7aa33a375a76f05118d4 Mon Sep 17 00:00:00 2001 From: jsmitrah Date: Wed, 29 Jul 2026 17:55:38 +0530 Subject: [PATCH 3/4] fixed the lint error. --- .../stories/Table.stories.tsx | 73 ------------------- 1 file changed, 73 deletions(-) diff --git a/packages/react-aria-components/stories/Table.stories.tsx b/packages/react-aria-components/stories/Table.stories.tsx index 85ce3b39cf9..3e41436acf9 100644 --- a/packages/react-aria-components/stories/Table.stories.tsx +++ b/packages/react-aria-components/stories/Table.stories.tsx @@ -988,79 +988,6 @@ export const OnLoadMoreTableStory: StoryObj = { } }; -const InitialFocusExample = (args: { - initialFocus?: 'row' | 'columnheader'; - selectionMode?: 'none' | 'single' | 'multiple'; -}) => ( -
- - - {args.selectionMode !== 'none' && ( - - - - )} - - Name - - Type - Date Modified - - - - {args.selectionMode !== 'none' && ( - - - - )} - Games - File folder - 6/7/2020 - - - {args.selectionMode !== 'none' && ( - - - - )} - Program Files - File folder - 4/7/2021 - - - {args.selectionMode !== 'none' && ( - - - - )} - bootmgr - System file - 11/20/2010 - - -
-
-); - -export const InitialFocusExampleStory: StoryObj = { - render: InitialFocusExample, - name: 'initialFocus="columnheader" with selection checkbox column', - args: { - initialFocus: 'columnheader', - selectionMode: 'multiple' - }, - argTypes: { - initialFocus: { - control: 'radio', - options: ['row', 'columnheader'] - }, - selectionMode: { - control: 'radio', - options: ['none', 'single', 'multiple'] - } - } -}; - export const VirtualizedTable: TableStory = () => { let items: {id: number; foo: string; bar: string; baz: string}[] = []; for (let i = 0; i < 1000; i++) { From 72c324d92a492004ae68acf4e1c9f34d4b8b139d Mon Sep 17 00:00:00 2001 From: jsmitrah Date: Wed, 29 Jul 2026 18:38:49 +0530 Subject: [PATCH 4/4] fixed the lint error. --- .../stories/Table.stories.tsx | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/packages/react-aria-components/stories/Table.stories.tsx b/packages/react-aria-components/stories/Table.stories.tsx index 3e41436acf9..cc937dc61e6 100644 --- a/packages/react-aria-components/stories/Table.stories.tsx +++ b/packages/react-aria-components/stories/Table.stories.tsx @@ -988,6 +988,79 @@ export const OnLoadMoreTableStory: StoryObj = { } }; +const InitialFocusExample = (args: { + initialFocus?: 'row' | 'columnheader'; + selectionMode?: 'none' | 'single' | 'multiple'; +}) => ( +
+ + + {args.selectionMode !== 'none' && ( + + + + )} + + Name + + Type + Date Modified + + + + {args.selectionMode !== 'none' && ( + + + + )} + Games + File folder + 6/7/2020 + + + {args.selectionMode !== 'none' && ( + + + + )} + Program Files + File folder + 4/7/2021 + + + {args.selectionMode !== 'none' && ( + + + + )} + bootmgr + System file + 11/20/2010 + + +
+
+); + +export const InitialFocusExampleStory: StoryObj = { + render: InitialFocusExample, + name: 'initialFocus="columnheader" with selection checkbox column', + args: { + initialFocus: 'columnheader', + selectionMode: 'multiple' + }, + argTypes: { + initialFocus: { + control: 'radio', + options: ['row', 'columnheader'] + }, + selectionMode: { + control: 'radio', + options: ['none', 'single', 'multiple'] + } + } +}; + export const VirtualizedTable: TableStory = () => { let items: {id: number; foo: string; bar: string; baz: string}[] = []; for (let i = 0; i < 1000; i++) {