Skip to content

feat: (WIP) support Textfields in collection components#10159

Draft
LFDanLu wants to merge 14 commits into
mainfrom
textfield_gridlists
Draft

feat: (WIP) support Textfields in collection components#10159
LFDanLu wants to merge 14 commits into
mainfrom
textfield_gridlists

Conversation

@LFDanLu

@LFDanLu LFDanLu commented Jun 5, 2026

Copy link
Copy Markdown
Member

Closes

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

Test GridList/Table/Tree with textfield stories and test keyboardNavigationBehavior=tab behavior. You should be able to interact/type/etc with the interactive elements without causing focus navigation/row selection even if you use the arrow keys/Enter/Space/etc. It should require Tab from the cell/row to the cell/row's content to interact with the children, and Shift-Tab back to the cell/row to perform row navigation/selection via arrow keys/Enter/Space as usual.

Test the same in S2 TableView/CardView/TagGroup. Be sure to test TableView more throughly since keyboardNavigationBehavior=tab is net new as well

🧢 Your Project:

RSP

@rspbot

rspbot commented Jun 5, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 5, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 8, 2026

Copy link
Copy Markdown

@snowystinger snowystinger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CardView story. Tab into Card with textfield, open menu, press Esc, focus returns to Card not menu trigger.
I think focus return was something we knew we'd struggle with. We have some open issues about this when Shift+Tab'ing back to a collection.
Looks like you noted this as well https://github.com/adobe/react-spectrum/pull/10159/changes#diff-b741e806d7e03bb7f42427dbe388c23128044ae2c96c612a9bceb9852ec408abR1021

Should hitting Esc in a TextField of a selected card de-select it? I thought we were ignoring events that came from inside a cell? basically, if the event's target was anything other than a data-collection* element, then it should be ignored.
In a similar vein, fn+arrow causes grid level navigation even when inside the textfield, which it shouldn't because those keys are shortcuts for moving the cursor inside the textfield already.


## Keyboard navigation

By default, GridList uses arrow key navigation to move focus into rows. Set `keyboardNavigationBehavior="tab"` to have <Keyboard>Tab</Keyboard> move focus in and out of a row.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should include a blurb about when you'd be likely to want to change the keyboard navigation behavior, ie you have something like a textfield or something else that would conflict with arrow key navigation


// TODO: a change in behavior since taggroup is a gridlist with "tab" keyboard navigation behavior
// previously you could go to the next tab via arrow keys when you were focused on the close button
await user.keyboard('{Shift>}{Tab}{/Shift}');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Suggested change
await user.keyboard('{Shift>}{Tab}{/Shift}');
await user.tab({shift: true});

Do you think Tags should be special and differ on this? Not like they can have anything other than the button inside them? or do you think editable tags could be a thing?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it might be odd to make an exception since it muddies the waters w/ regards to expected behavior. I do feel like editable tabs could be a thing, but I'm not sure if the experience would be more like our editable table or with an inline textfield

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some other exceptions/special cases that we will want to discuss:

  • selection checkboxes in Table, feels a bit awkward that you have to tab into them now
  • drag handles, same case as the checkboxes
  • table column menus
  • do we want to change the editable table row experience to use inline textfields now that is supported?

// TODO: guarding against selection when firing space/enter/click on a element in a row is technically not only limited to textfields so I
// am not making it specific to keyboardNavigationBehavior = tab, but maybe we should still?
// we need to guard against space/enter triggering selection/row link via usePress (from itemProps) so check if propagation
// is stopped. this also fixes space not working in a textfield in a tree parent row

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also fixes space not working in a textfield in a tree parent row

was this not fixed in my other PRs for event leaks? I'm not following the issue here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't check against your PR since I wanted to approach this in a more isolated approach to see what was the minimal set of changes I needed.

The issue was that useSelectableItem returns itemProps that include onKeyDown that handles updating row selection that then gets merged with our onKeyDown via mergeProps below. That meant hitting Enter in a textfield/area in a row also toggled selection

role: 'row',
onKeyDownCapture,
onKeyDown,
onKeyDownCapture: keyboardNavigationBehavior === 'arrow' ? onKeyDownCapture : undefined,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to move this one away from capturing, maybe that will simplify, I was going to look at this in keyboard-shortcut-handler...new-event-leaks#diff-3989d5920106dcc67dc6bdd9497aac4530373a27f5cc521fbee771d773650b92R171

@LFDanLu

LFDanLu commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

@snowystinger with regards to

Should hitting Esc in a TextField of a selected card de-select it? I thought we were ignoring events that came from inside a cell? basically, if the event's target was anything other than a data-collection* element, then it should be ignored.

I agree, I made a comment here wondering if we should automatically stop propagation on ALL events if they bubbled up from the cell. I can update the code to do so, but still a bit wary about if its too much of a blanket approach.

EDIT:
We will actually still need to let Tab pass through so the useSelectableCollection Tab handler still runs when attempting to tab out from the collection

@rspbot

rspbot commented Jun 9, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 9, 2026

Copy link
Copy Markdown
## API Changes

react-aria-components

/react-aria-components:Table

 Table {
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   children?: ReactNode
   className?: ClassNameOrFunction<TableRenderProps> = 'react-aria-Table'
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   dragAndDropHooks?: DragAndDropHooks
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   onExpandedChange?: (Set<Key>) => any
   onRowAction?: (Key) => void
   onSelectionChange?: (Selection) => void
   onSortChange?: (SortDescriptor) => any
   selectedKeys?: 'all' | Iterable<Key>
   selectionBehavior?: SelectionBehavior = 'toggle'
   selectionMode?: SelectionMode
   shouldSelectOnPressUp?: boolean
   slot?: string | null
   sortDescriptor?: SortDescriptor
   style?: StyleOrFunction<TableRenderProps>
   treeColumn?: Key
 }

/react-aria-components:Tree

 Tree <T> {
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean | FocusStrategy
   children?: ReactNode | (T) => ReactNode
   className?: ClassNameOrFunction<TreeRenderProps> = 'react-aria-Tree'
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   dependencies?: ReadonlyArray<any>
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   dragAndDropHooks?: DragAndDropHooks<NoInfer<T>>
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
   id?: string
   items?: Iterable<T>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   onAction?: (Key) => void
   onExpandedChange?: (Set<Key>) => any
   onSelectionChange?: (Selection) => void
   render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, TreeRenderProps>
   selectedKeys?: 'all' | Iterable<Key>
   selectionBehavior?: SelectionBehavior = 'toggle'
   selectionMode?: SelectionMode
   shouldSelectOnPressUp?: boolean
   slot?: string | null
   style?: StyleOrFunction<TreeRenderProps>
 }

/react-aria-components:TableProps

 TableProps {
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   children?: ReactNode
   className?: ClassNameOrFunction<TableRenderProps> = 'react-aria-Table'
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   dragAndDropHooks?: DragAndDropHooks
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   onExpandedChange?: (Set<Key>) => any
   onRowAction?: (Key) => void
   onSelectionChange?: (Selection) => void
   onSortChange?: (SortDescriptor) => any
   selectedKeys?: 'all' | Iterable<Key>
   selectionBehavior?: SelectionBehavior = 'toggle'
   selectionMode?: SelectionMode
   shouldSelectOnPressUp?: boolean
   slot?: string | null
   sortDescriptor?: SortDescriptor
   style?: StyleOrFunction<TableRenderProps>
   treeColumn?: Key
 }

/react-aria-components:TreeProps

 TreeProps <T> {
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean | FocusStrategy
   children?: ReactNode | (T) => ReactNode
   className?: ClassNameOrFunction<TreeRenderProps> = 'react-aria-Tree'
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   dependencies?: ReadonlyArray<any>
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   dragAndDropHooks?: DragAndDropHooks<NoInfer<T>>
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
   id?: string
   items?: Iterable<T>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   onAction?: (Key) => void
   onExpandedChange?: (Set<Key>) => any
   onSelectionChange?: (Selection) => void
   render?: DOMRenderFunction<keyof React.JSX.IntrinsicElements, TreeRenderProps>
   selectedKeys?: 'all' | Iterable<Key>
   selectionBehavior?: SelectionBehavior = 'toggle'
   selectionMode?: SelectionMode
   shouldSelectOnPressUp?: boolean
   slot?: string | null
   style?: StyleOrFunction<TreeRenderProps>
 }

@react-aria/grid

/@react-aria/grid:GridProps

 GridProps {
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   disallowTypeAhead?: boolean = false
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   focusMode?: 'row' | 'cell' = 'row'
   getRowText?: (Key) => string = (key) => state.collection.getItem(key)?.textValue
   id?: string
   isVirtualized?: boolean
   keyboardDelegate?: KeyboardDelegate
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   onCellAction?: (Key) => void
   onRowAction?: (Key) => void
   scrollRef?: RefObject<HTMLElement | null>
   shouldSelectOnPressUp?: boolean

@react-aria/interactions

/@react-aria/interactions:KeyboardProps

 KeyboardProps {
-  allowComposing?: boolean
-  allowRepeats?: boolean
   isDisabled?: boolean
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
-  shortcuts?: KeyboardShortcutBindings
 }

@react-aria/table

/@react-aria/table:AriaTableProps

 AriaTableProps {
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   disallowTypeAhead?: boolean = false
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   focusMode?: 'row' | 'cell' = 'row'
   getRowText?: (Key) => string = (key) => state.collection.getItem(key)?.textValue
   id?: string
   isVirtualized?: boolean
   keyboardDelegate?: KeyboardDelegate
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   layoutDelegate?: LayoutDelegate
   onCellAction?: (Key) => void
   onRowAction?: (Key) => void
   scrollRef?: RefObject<HTMLElement | null>
 }

@react-aria/tree

/@react-aria/tree:AriaTreeProps

 AriaTreeProps <T> {
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean | FocusStrategy
   children: CollectionChildren<T>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   id?: string
   items?: Iterable<T>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   onAction?: (Key) => void
   onSelectionChange?: (Selection) => void
   selectedKeys?: 'all' | Iterable<Key>
   selectionMode?: SelectionMode
 }

@react-spectrum/s2

/@react-spectrum/s2:TableView

 TableView {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   children?: ReactNode
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   density?: 'compact' | 'spacious' | 'regular' = 'regular'
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   dragAndDropHooks?: DragAndDropHooks
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
   id?: string
   isQuiet?: boolean
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   loadingState?: LoadingState
   onAction?: (Key) => void
   onExpandedChange?: (Set<Key>) => any
   onLoadMore?: () => any
   onResizeEnd?: (Map<Key, ColumnSize>) => void
   onResizeStart?: (Map<Key, ColumnSize>) => void
   onSelectionChange?: (Selection) => void
   onSortChange?: (SortDescriptor) => any
   overflowMode?: 'wrap' | 'truncate' = 'truncate'
   renderActionBar?: ('all' | Set<Key>) => ReactElement
   selectedKeys?: 'all' | Iterable<Key>
   selectionMode?: SelectionMode
   selectionStyle?: 'checkbox' | 'highlight' = 'checkbox'
   shouldSelectOnPressUp?: boolean
   slot?: string | null
   sortDescriptor?: SortDescriptor
   styles?: StylesPropWithHeight
   treeColumn?: Key
 }

/@react-spectrum/s2:TableViewProps

 TableViewProps {
   UNSAFE_className?: UnsafeClassName
   UNSAFE_style?: CSSProperties
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   children?: ReactNode
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   density?: 'compact' | 'spacious' | 'regular' = 'regular'
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   dragAndDropHooks?: DragAndDropHooks
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
   id?: string
   isQuiet?: boolean
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   loadingState?: LoadingState
   onAction?: (Key) => void
   onExpandedChange?: (Set<Key>) => any
   onLoadMore?: () => any
   onResizeEnd?: (Map<Key, ColumnSize>) => void
   onResizeStart?: (Map<Key, ColumnSize>) => void
   onSelectionChange?: (Selection) => void
   onSortChange?: (SortDescriptor) => any
   overflowMode?: 'wrap' | 'truncate' = 'truncate'
   renderActionBar?: ('all' | Set<Key>) => ReactElement
   selectedKeys?: 'all' | Iterable<Key>
   selectionMode?: SelectionMode
   selectionStyle?: 'checkbox' | 'highlight' = 'checkbox'
   shouldSelectOnPressUp?: boolean
   slot?: string | null
   sortDescriptor?: SortDescriptor
   styles?: StylesPropWithHeight
   treeColumn?: Key
 }

@react-spectrum/table

/@react-spectrum/table:TableView

 TableView <T extends {}> {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   alignSelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'center' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'stretch'>
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   bottom?: Responsive<DimensionValue>
   children: [ReactElement<TableHeaderProps<T>>, ReactElement<TableBodyProps<T>>]
   defaultSelectedKeys?: 'all' | Iterable<Key>
   density?: 'compact' | 'regular' | 'spacious' = 'regular'
   disabledBehavior?: DisabledBehavior = 'selection'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   dragAndDropHooks?: DragAndDropHooks<NoInfer<{}>>['dragAndDropHooks']
   end?: Responsive<DimensionValue>
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   flex?: Responsive<string | number | boolean>
   flexBasis?: Responsive<number | string>
   flexGrow?: Responsive<number>
   flexShrink?: Responsive<number>
   gridArea?: Responsive<string>
   gridColumn?: Responsive<string>
   gridColumnEnd?: Responsive<string>
   gridColumnStart?: Responsive<string>
   gridRow?: Responsive<string>
   gridRowEnd?: Responsive<string>
   gridRowStart?: Responsive<string>
   height?: Responsive<DimensionValue>
   id?: string
   isHidden?: Responsive<boolean>
   isQuiet?: boolean
   justifySelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'center' | 'left' | 'right' | 'stretch'>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   left?: Responsive<DimensionValue>
   margin?: Responsive<DimensionValue>
   marginBottom?: Responsive<DimensionValue>
   marginEnd?: Responsive<DimensionValue>
   marginTop?: Responsive<DimensionValue>
   marginX?: Responsive<DimensionValue>
   marginY?: Responsive<DimensionValue>
   maxHeight?: Responsive<DimensionValue>
   maxWidth?: Responsive<DimensionValue>
   minHeight?: Responsive<DimensionValue>
   minWidth?: Responsive<DimensionValue>
   onAction?: (Key) => void
   onResize?: (Map<Key, ColumnSize>) => void
   onResizeEnd?: (Map<Key, ColumnSize>) => void
   onResizeStart?: (Map<Key, ColumnSize>) => void
   onSelectionChange?: (Selection) => void
   onSortChange?: (SortDescriptor) => any
   order?: Responsive<number>
   overflowMode?: 'wrap' | 'truncate' = 'truncate'
   position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
   renderEmptyState?: () => JSX.Element
   right?: Responsive<DimensionValue>
   selectedKeys?: 'all' | Iterable<Key>
   selectionMode?: SelectionMode
   selectionStyle?: 'checkbox' | 'highlight'
   shouldSelectOnPressUp?: boolean
   sortDescriptor?: SortDescriptor
   start?: Responsive<DimensionValue>
   top?: Responsive<DimensionValue>
   width?: Responsive<DimensionValue>
   zIndex?: Responsive<number>
 }

/@react-spectrum/table:SpectrumTableProps

 SpectrumTableProps <T> {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   alignSelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'center' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'stretch'>
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   bottom?: Responsive<DimensionValue>
   children: [ReactElement<TableHeaderProps<T>>, ReactElement<TableBodyProps<T>>]
   defaultSelectedKeys?: 'all' | Iterable<Key>
   density?: 'compact' | 'regular' | 'spacious' = 'regular'
   disabledBehavior?: DisabledBehavior = 'selection'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   dragAndDropHooks?: DragAndDropHooks<NoInfer<T>>['dragAndDropHooks']
   end?: Responsive<DimensionValue>
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   flex?: Responsive<string | number | boolean>
   flexBasis?: Responsive<number | string>
   flexGrow?: Responsive<number>
   flexShrink?: Responsive<number>
   gridArea?: Responsive<string>
   gridColumn?: Responsive<string>
   gridColumnEnd?: Responsive<string>
   gridColumnStart?: Responsive<string>
   gridRow?: Responsive<string>
   gridRowEnd?: Responsive<string>
   gridRowStart?: Responsive<string>
   height?: Responsive<DimensionValue>
   id?: string
   isHidden?: Responsive<boolean>
   isQuiet?: boolean
   justifySelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'center' | 'left' | 'right' | 'stretch'>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   left?: Responsive<DimensionValue>
   margin?: Responsive<DimensionValue>
   marginBottom?: Responsive<DimensionValue>
   marginEnd?: Responsive<DimensionValue>
   marginTop?: Responsive<DimensionValue>
   marginX?: Responsive<DimensionValue>
   marginY?: Responsive<DimensionValue>
   maxHeight?: Responsive<DimensionValue>
   maxWidth?: Responsive<DimensionValue>
   minHeight?: Responsive<DimensionValue>
   minWidth?: Responsive<DimensionValue>
   onAction?: (Key) => void
   onResize?: (Map<Key, ColumnSize>) => void
   onResizeEnd?: (Map<Key, ColumnSize>) => void
   onResizeStart?: (Map<Key, ColumnSize>) => void
   onSelectionChange?: (Selection) => void
   onSortChange?: (SortDescriptor) => any
   order?: Responsive<number>
   overflowMode?: 'wrap' | 'truncate' = 'truncate'
   position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
   renderEmptyState?: () => JSX.Element
   right?: Responsive<DimensionValue>
   selectedKeys?: 'all' | Iterable<Key>
   selectionMode?: SelectionMode
   selectionStyle?: 'checkbox' | 'highlight'
   shouldSelectOnPressUp?: boolean
   sortDescriptor?: SortDescriptor
   start?: Responsive<DimensionValue>
   top?: Responsive<DimensionValue>
   width?: Responsive<DimensionValue>
   zIndex?: Responsive<number>
 }

@react-spectrum/tree

/@react-spectrum/tree:TreeView

 TreeView <T extends {}> {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   alignSelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'center' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'stretch'>
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean | FocusStrategy
   bottom?: Responsive<DimensionValue>
   children?: ReactNode | ({}) => ReactNode
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   end?: Responsive<DimensionValue>
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
   flex?: Responsive<string | number | boolean>
   flexBasis?: Responsive<number | string>
   flexGrow?: Responsive<number>
   flexShrink?: Responsive<number>
   gridArea?: Responsive<string>
   gridColumn?: Responsive<string>
   gridColumnEnd?: Responsive<string>
   gridColumnStart?: Responsive<string>
   gridRow?: Responsive<string>
   gridRowEnd?: Responsive<string>
   gridRowStart?: Responsive<string>
   height?: Responsive<DimensionValue>
   id?: string
   isHidden?: Responsive<boolean>
   items?: Iterable<T>
   justifySelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'center' | 'left' | 'right' | 'stretch'>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   left?: Responsive<DimensionValue>
   margin?: Responsive<DimensionValue>
   marginBottom?: Responsive<DimensionValue>
   marginEnd?: Responsive<DimensionValue>
   marginTop?: Responsive<DimensionValue>
   marginX?: Responsive<DimensionValue>
   marginY?: Responsive<DimensionValue>
   maxHeight?: Responsive<DimensionValue>
   maxWidth?: Responsive<DimensionValue>
   minHeight?: Responsive<DimensionValue>
   minWidth?: Responsive<DimensionValue>
   onAction?: (Key) => void
   onExpandedChange?: (Set<Key>) => any
   onSelectionChange?: (Selection) => void
   order?: Responsive<number>
   position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
   renderEmptyState?: () => JSX.Element
   right?: Responsive<DimensionValue>
   selectedKeys?: 'all' | Iterable<Key>
   selectionMode?: SelectionMode
   selectionStyle?: 'checkbox' | 'highlight'
   shouldSelectOnPressUp?: boolean
   start?: Responsive<DimensionValue>
   top?: Responsive<DimensionValue>
   width?: Responsive<DimensionValue>
   zIndex?: Responsive<number>
 }

/@react-spectrum/tree:SpectrumTreeViewProps

 SpectrumTreeViewProps <T> {
   UNSAFE_className?: string
   UNSAFE_style?: CSSProperties
   alignSelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'center' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'stretch'>
   aria-describedby?: string
   aria-details?: string
   aria-label?: string
   aria-labelledby?: string
   autoFocus?: boolean | FocusStrategy
   bottom?: Responsive<DimensionValue>
   children?: ReactNode | (T) => ReactNode
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   disabledBehavior?: DisabledBehavior = 'all'
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   end?: Responsive<DimensionValue>
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
   flex?: Responsive<string | number | boolean>
   flexBasis?: Responsive<number | string>
   flexGrow?: Responsive<number>
   flexShrink?: Responsive<number>
   gridArea?: Responsive<string>
   gridColumn?: Responsive<string>
   gridColumnEnd?: Responsive<string>
   gridColumnStart?: Responsive<string>
   gridRow?: Responsive<string>
   gridRowEnd?: Responsive<string>
   gridRowStart?: Responsive<string>
   height?: Responsive<DimensionValue>
   id?: string
   isHidden?: Responsive<boolean>
   items?: Iterable<T>
   justifySelf?: Responsive<'auto' | 'normal' | 'start' | 'end' | 'flex-start' | 'flex-end' | 'self-start' | 'self-end' | 'center' | 'left' | 'right' | 'stretch'>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   left?: Responsive<DimensionValue>
   margin?: Responsive<DimensionValue>
   marginBottom?: Responsive<DimensionValue>
   marginEnd?: Responsive<DimensionValue>
   marginTop?: Responsive<DimensionValue>
   marginX?: Responsive<DimensionValue>
   marginY?: Responsive<DimensionValue>
   maxHeight?: Responsive<DimensionValue>
   maxWidth?: Responsive<DimensionValue>
   minHeight?: Responsive<DimensionValue>
   minWidth?: Responsive<DimensionValue>
   onAction?: (Key) => void
   onExpandedChange?: (Set<Key>) => any
   onSelectionChange?: (Selection) => void
   order?: Responsive<number>
   position?: Responsive<'static' | 'relative' | 'absolute' | 'fixed' | 'sticky'>
   renderEmptyState?: () => JSX.Element
   right?: Responsive<DimensionValue>
   selectedKeys?: 'all' | Iterable<Key>
   selectionMode?: SelectionMode
   selectionStyle?: 'checkbox' | 'highlight'
   shouldSelectOnPressUp?: boolean
   start?: Responsive<DimensionValue>
   top?: Responsive<DimensionValue>
   width?: Responsive<DimensionValue>
   zIndex?: Responsive<number>
 }

@react-stately/table

/@react-stately/table:TableProps

 TableProps <T> {
   children: [ReactElement<TableHeaderProps<T>>, ReactElement<TableBodyProps<T>>]
   defaultExpandedKeys?: Iterable<Key>
   defaultSelectedKeys?: 'all' | Iterable<Key>
   disabledKeys?: Iterable<Key>
   disallowEmptySelection?: boolean
   escapeKeyBehavior?: 'clearSelection' | 'none' = 'clearSelection'
   expandedKeys?: Iterable<Key>
+  keyboardNavigationBehavior?: 'arrow' | 'tab' = 'arrow'
   onExpandedChange?: (Set<Key>) => any
   onSelectionChange?: (Selection) => void
   onSortChange?: (SortDescriptor) => any
   selectedKeys?: 'all' | Iterable<Key>
   shouldSelectOnPressUp?: boolean
   sortDescriptor?: SortDescriptor
   treeColumn?: Key
 }

@rspbot

rspbot commented Jun 9, 2026

Copy link
Copy Markdown

Agent Skills Changes

Modified (10)
Install

React Spectrum S2:

npx skills add https://d1pzu54gtk2aed.cloudfront.net/pr/ef4718659c927ff9c33eadb9a2663a9194cc7653/

React Aria:

npx skills add https://d5iwopk28bdhl.cloudfront.net/pr/ef4718659c927ff9c33eadb9a2663a9194cc7653/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants