Skip to content

feat: TextFields in GridList and Grid #8691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ index 39a24b8f2ccdc52739d130480ab18975073616cb..0c3f5199401c15b90230c25a02de364e
}
UI.clearInitialValue(el);
}
diff --git a/dist/cjs/event/behavior/keydown.js b/dist/cjs/event/behavior/keydown.js
index 55027cb256f66b808d17280dc01bc55a796a1032..993d5de5a838a711d7ae009344354772a42ed0c1 100644
--- a/dist/cjs/event/behavior/keydown.js
+++ b/dist/cjs/event/behavior/keydown.js
@@ -110,7 +110,7 @@ const keydownBehavior = {
},
Tab: (event, target, instance)=>{
return ()=>{
- const dest = getTabDestination.getTabDestination(target, instance.system.keyboard.modifiers.Shift);
+ const dest = getTabDestination.getTabDestination(document.activeElement, instance.system.keyboard.modifiers.Shift);
focus.focusElement(dest);
if (selection.hasOwnSelection(dest)) {
UI.setUISelection(dest, {
diff --git a/dist/cjs/utils/focus/getActiveElement.js b/dist/cjs/utils/focus/getActiveElement.js
index d25f3a8ef67e856e43614559f73012899c0b53d7..4ed9ee45565ed438ee9284d8d3043c0bd50463eb 100644
--- a/dist/cjs/utils/focus/getActiveElement.js
Expand Down
54 changes: 49 additions & 5 deletions packages/@react-aria/grid/src/useGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
* governing permissions and limitations under the License.
*/

import {AriaLabelingProps, DOMAttributes, DOMProps, Key, KeyboardDelegate, RefObject} from '@react-types/shared';
import {AriaLabelingProps, BaseEvent, DOMAttributes, DOMProps, Key, KeyboardDelegate, RefObject} from '@react-types/shared';
import {filterDOMProps, mergeProps, useId} from '@react-aria/utils';
import {GridCollection} from '@react-types/grid';
import {GridKeyboardDelegate} from './GridKeyboardDelegate';
import {gridMap} from './utils';
import {GridState} from '@react-stately/grid';
import {useCallback, useMemo} from 'react';
import {KeyboardEvent, useCallback, useMemo} from 'react';
import {useCollator, useLocale} from '@react-aria/i18n';
import {useGridSelectionAnnouncement} from './useGridSelectionAnnouncement';
import {useHasTabbableChild} from '@react-aria/focus';
Expand Down Expand Up @@ -64,7 +64,13 @@ export interface GridProps extends DOMProps, AriaLabelingProps {
*/
escapeKeyBehavior?: 'clearSelection' | 'none',
/** Whether selection should occur on press up instead of press down. */
shouldSelectOnPressUp?: boolean
shouldSelectOnPressUp?: boolean,
/**
* Whether keyboard navigation to focusable elements within grid list items is
* via the left/right arrow keys or the tab key.
* @default 'arrow'
*/
keyboardNavigationBehavior?: 'arrow' | 'tab'
Copy link
Member

Choose a reason for hiding this comment

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

Wondering if this API should be at the cell level instead of (or in addition to?) the whole grid. I could imagine a UI where only certain columns have textfields in them, and it would be nice if the other columns still automatically focused the content of the cell. For example, in the Reddit Table story in S2 with keyboardNavigationBehavior="tab", now you have to tab to the links instead of just arrowing to them, but there are no inputs in that cell.

Ideally we'd even automatically set a cell to tab behavior if we detected inputs in them. But I guess there will be cases with custom components where we'll need a prop too.

Copy link
Contributor

Choose a reason for hiding this comment

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

Linking related discussion #8691 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, I don't love it either, I have to tab to the column header menu trigger now as well for resizing table, and the focus ring on selection checkbox cells looks funny.

}

export interface GridAria {
Expand All @@ -90,7 +96,8 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
onRowAction,
onCellAction,
escapeKeyBehavior = 'clearSelection',
shouldSelectOnPressUp
shouldSelectOnPressUp,
keyboardNavigationBehavior = 'arrow'
Copy link
Contributor

@nwidynski nwidynski Aug 8, 2025

Choose a reason for hiding this comment

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

One of the insights while working on this was that "edit mode", when handling events in the scroll container, was pretty much just keyboardNavigationBehavior="tab" applied conditionally.

Raising here because I think it could make sense to lift this prop into grid state instead of forwarding via props, which could replace state.isKeyboardNavigationDisabled of table and grid.

For impl, see useGridEditState & useGridState

Copy link
Member Author

@snowystinger snowystinger Aug 12, 2025

Choose a reason for hiding this comment

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

Edit: started playing around with resizable table columns, I'm definitely going to need to think more about this

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you clarify in your changes how this decided a cell was editable? I don't think I'm following that right.

I implemented an extended SelectionManager for the editable grid, called a GridManager. You can checkout the impl here. TLDR; isEditable was set as a node prop on either column or cell.

} = props;
let {selectionManager: manager} = state;

Expand Down Expand Up @@ -123,8 +130,45 @@ export function useGrid<T>(props: GridProps, state: GridState<T, GridCollection<
escapeKeyBehavior
});


let originalOnKeyDown = collectionProps.onKeyDown;
let onKeyDown = useCallback((e: BaseEvent<KeyboardEvent<any>>) => {
Copy link
Member

Choose a reason for hiding this comment

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

I don't follow what these event handlers are doing. Why the special cases? Seems quite specific to our implementation

Copy link
Member Author

Choose a reason for hiding this comment

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

I was trying to avoid passing keyboardNavigationBehavior to useSelectableCollection
I couldn't necessarily pass it to the GridKeyboardDelegate either since that can be passed in, but that was another thought I had.

I know this won't be the final logic for the PR, but it serves as a starting point since I'm unsure what we want to do here. So this shows where there are problematic event handlers.

@nwidynski had this suggestion #8691 (comment)

But there are a few ways we could approach this.

The problematic behaviours that this is blocking are:
useSelectableCollection encompasses type select, which is capturing, and moving focus out of the collection on bubble phase tab.

if (keyboardNavigationBehavior === 'tab') {
if (e.target === ref.current
|| (e.target as HTMLElement).getAttribute('role') === 'gridcell'
|| (e.target as HTMLElement).getAttribute('role') === 'row'
|| (e.target as HTMLElement).getAttribute('role') === 'presentation' // special case for gridlist thanks to redispatch of keyboard event by gridcell, theoretically no other presentation elements should get focus
|| (e.target instanceof HTMLInputElement && (e.target as HTMLInputElement).getAttribute('type') === 'checkbox')
|| e.key === 'Tab') {
originalOnKeyDown?.(e);
}
}
if (keyboardNavigationBehavior !== 'tab') {
originalOnKeyDown?.(e);
}
}, [originalOnKeyDown, keyboardNavigationBehavior, ref]);
collectionProps.onKeyDown = onKeyDown;

let originalOnKeyDownCapture = collectionProps.onKeyDownCapture;
let onKeyDownCapture = useCallback((e: BaseEvent<KeyboardEvent<any>>) => {
if (keyboardNavigationBehavior === 'tab') {
if (e.target === ref.current
|| (e.target as HTMLElement).getAttribute('role') === 'gridcell'
|| (e.target as HTMLElement).getAttribute('role') === 'row'
|| (e.target as HTMLElement).getAttribute('role') === 'presentation' // special case for gridlist thanks to redispatch of keyboard event by gridcell, theoretically no other presentation elements should get focus
|| (e.target instanceof HTMLInputElement && (e.target as HTMLInputElement).getAttribute('type') === 'checkbox')
|| e.key === 'Tab') {
originalOnKeyDownCapture?.(e);
}
}
if (keyboardNavigationBehavior !== 'tab') {
originalOnKeyDownCapture?.(e);
}
}, [originalOnKeyDownCapture, keyboardNavigationBehavior, ref]);
collectionProps.onKeyDownCapture = onKeyDownCapture;

let id = useId(props.id);
gridMap.set(state, {keyboardDelegate: delegate, actions: {onRowAction, onCellAction}, shouldSelectOnPressUp});
gridMap.set(state, {keyboardDelegate: delegate, keyboardNavigationBehavior, actions: {onRowAction, onCellAction}, shouldSelectOnPressUp});

let descriptionProps = useHighlightSelectionDescription({
selectionManager: manager,
Expand Down
57 changes: 54 additions & 3 deletions packages/@react-aria/grid/src/useGridCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {DOMAttributes, FocusableElement, Key, RefObject} from '@react-types/shared';
import {focusSafely, isFocusVisible} from '@react-aria/interactions';
import {getFocusableTreeWalker} from '@react-aria/focus';
import {getScrollParent, mergeProps, scrollIntoViewport} from '@react-aria/utils';
import {getScrollParent, isFocusable, mergeProps, scrollIntoViewport} from '@react-aria/utils';
import {GridCollection, GridNode} from '@react-types/grid';
import {gridMap} from './utils';
import {GridState} from '@react-stately/grid';
Expand Down Expand Up @@ -62,7 +62,10 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
} = props;

let {direction} = useLocale();
let {keyboardDelegate, actions: {onCellAction}} = gridMap.get(state)!;
let {keyboardDelegate, keyboardNavigationBehavior, actions: {onCellAction}} = gridMap.get(state)!;
if (keyboardNavigationBehavior === 'tab') {
focusMode = 'cell';
}
Comment on lines +66 to +68
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be a hard override? IIRC, my impl had

focusMode ??= keyboardNavigation === 'tab' ? 'cell' : 'child';

Copy link
Member Author

Choose a reason for hiding this comment

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

a good point, hadn't thought this through yet

Copy link
Member Author

Choose a reason for hiding this comment

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

Trying to think of the mixed use case, I was thinking maybe for the selection column in a table. But it's a little weird to mix the navigation styles, feels a little inconsistent even if i don't like the way the selection column ends up looking in React Spectrum. What was your reason for doing it not as a hard override?

Copy link
Contributor

Choose a reason for hiding this comment

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

Imagine a cell with a text field and "save" button. When pressing on the cell, wouldn't you want to have the textfield focused for convenience?


// We need to track the key of the item at the time it was last focused so that we force
// focus to go to the item when the DOM node is reused for a different item in a virtualizer.
Expand Down Expand Up @@ -113,6 +116,10 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
return;
}

if (keyboardNavigationBehavior === 'tab' && e.target !== ref.current) {
Copy link
Contributor

Choose a reason for hiding this comment

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

L115: I think state.isKeyboardNavigationBehaviorDisabled shouldn't interfere with editing?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm sorry, I don't follow your concern here, both are possible conditions for leaving this event handler early?

Copy link
Contributor

@nwidynski nwidynski Aug 12, 2025

Choose a reason for hiding this comment

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

Nevermind, you are correct. I had removed this condition for the W3C focus wrapping feature.

See here for impl.

return;
}

let walker = getFocusableTreeWalker(ref.current);
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this should only walk tabbable, right? Same in L78.

We need to add tests for disabled interactive content 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

This is for cell level navigation, not inside the cell. In keyboardNavigationBehavior = tab, we've already exited if we were inside the cell, skipping this code. I think it is correct as it is, but maybe an example would be helpful.

Copy link
Member Author

Choose a reason for hiding this comment

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

added a test for disabled interactive content

Copy link
Contributor

@nwidynski nwidynski Aug 12, 2025

Choose a reason for hiding this comment

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

I know this isn't related to edit, IIRC they were bugs I fixed while working on it. L78 is basically the focus strategy of the cell, which should honor whether a child is tabbable. L123 is for keyboardNavigationBehavior=arrow movement inside the cell, which also does not respect tabbable at the moment.

walker.currentNode = document.activeElement;

Expand Down Expand Up @@ -171,7 +178,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
? walker.previousNode() as FocusableElement
: walker.nextNode() as FocusableElement;

if (focusMode === 'child' && focusable === ref.current) {
if ((focusMode === 'child' && focusable === ref.current) || (keyboardNavigationBehavior === 'tab')) {
Copy link
Member

Choose a reason for hiding this comment

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

This will never happen because of the early return above right?

Copy link
Member Author

Choose a reason for hiding this comment

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

It happens if you are focused on the cell, the early return above is only for events inside the cell

focusable = null;
}

Expand All @@ -186,6 +193,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
// We prevent the capturing event from reaching children of the cell, e.g. pickers.
// We want arrow keys to navigate to the next cell instead. We need to re-dispatch
// the event from a higher parent so it still bubbles and gets handled by useSelectableCollection.
// Note: This may dispatch on something that isn't the cell nor the row, so we will need to handle that in useGrid.
ref.current.parentElement?.dispatchEvent(
new KeyboardEvent(e.nativeEvent.type, e.nativeEvent)
);
Expand Down Expand Up @@ -224,6 +232,48 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
}
};

let onKeyDown = (e) => {
if (!e.currentTarget.contains(e.target as Element) || !ref.current || !document.activeElement) {
return;
}

switch (e.key) {
case 'Tab': {
if (keyboardNavigationBehavior === 'tab') {
// If there is another focusable element within this item, stop propagation so the tab key
// is handled by the browser and not by useSelectableCollection (which would take us out of the list).
let walker = getFocusableTreeWalker(ref.current, {tabbable: true});
walker.currentNode = document.activeElement;
let next = e.shiftKey ? walker.previousNode() : walker.nextNode();

if (next) {
e.stopPropagation();
}
}
}
}
};

// Prevent pressing space to select the row.
let originalOnKeyDown = itemProps.onKeyDown;
itemProps.onKeyDown = (e) => {
if (keyboardNavigationBehavior === 'tab' && e.key === ' ' && e.target !== ref.current) {
e.stopPropagation();
return;
}
originalOnKeyDown?.(e);
};

// Prevent clicking on a focusable element from selecting the row.
Copy link
Member

Choose a reason for hiding this comment

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

This one and the one above seem like they should be handled by the event leaks - i.e. stopping propagation within the component that handles the press.

Copy link
Member Author

Choose a reason for hiding this comment

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

agreed, these would go away, but this pr isn't fixing event leaks, see description

Note: this does not address the event leaking brought up in another issue. That work is still to come and may make this unnecessary, however, that touches a much wider set of components and hooks with much more testing needed.

I'm working on event leaks in another local branch at the moment, but it has some interesting issues to solve still.

let originalPointerDown = itemProps.onPointerDown;
itemProps.onPointerDown = (e) => {
if (keyboardNavigationBehavior === 'tab' && isFocusable(e.target as Element) && e.target !== ref.current) {
e.stopPropagation();
return;
}
originalPointerDown?.(e);
};

// Grid cells can have focusable elements inside them. In this case, focus should
// be marshalled to that element rather than focusing the cell itself.
let onFocus = (e) => {
Expand Down Expand Up @@ -253,6 +303,7 @@ export function useGridCell<T, C extends GridCollection<T>>(props: GridCellProps
let gridCellProps: DOMAttributes = mergeProps(itemProps, {
role: 'gridcell',
onKeyDownCapture,
onKeyDown,
'aria-colspan': node.colSpan,
'aria-colindex': node.colIndex != null ? node.colIndex + 1 : undefined, // aria-colindex is 1-based
colSpan: isVirtualized ? undefined : node.colSpan,
Expand Down
1 change: 1 addition & 0 deletions packages/@react-aria/grid/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {Key, KeyboardDelegate} from '@react-types/shared';

interface GridMapShared {
keyboardDelegate: KeyboardDelegate,
keyboardNavigationBehavior: 'arrow' | 'tab',
actions: {
onRowAction?: (key: Key) => void,
onCellAction?: (key: Key) => void
Expand Down
34 changes: 34 additions & 0 deletions packages/@react-aria/gridlist/src/useGridList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import {
AriaLabelingProps,
BaseEvent,
CollectionBase,
DisabledBehavior,
DOMAttributes,
Expand All @@ -24,6 +25,7 @@ import {
RefObject
} from '@react-types/shared';
import {filterDOMProps, mergeProps, useId} from '@react-aria/utils';
import {KeyboardEvent, useCallback} from 'react';
import {listMap} from './utils';
import {ListState} from '@react-stately/list';
import {useGridSelectionAnnouncement, useHighlightSelectionDescription} from '@react-aria/grid';
Expand Down Expand Up @@ -144,6 +146,38 @@ export function useGridList<T>(props: AriaGridListOptions<T>, state: ListState<T
escapeKeyBehavior
});

let originalOnKeyDown = listProps.onKeyDown;
let onKeyDown = useCallback((e: BaseEvent<KeyboardEvent<any>>) => {
Copy link
Member

Choose a reason for hiding this comment

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

same

if (keyboardNavigationBehavior === 'tab') {
if (e.target === ref.current
|| (e.target as HTMLElement).getAttribute('role') === 'gridcell'
|| (e.target as HTMLElement).getAttribute('role') === 'row'
|| e.key === 'Tab') {
originalOnKeyDown?.(e);
}
}
if (keyboardNavigationBehavior !== 'tab') {
originalOnKeyDown?.(e);
}
}, [originalOnKeyDown, keyboardNavigationBehavior, ref]);
listProps.onKeyDown = onKeyDown;

let originalOnKeyDownCapture = listProps.onKeyDownCapture;
let onKeyDownCapture = useCallback((e: BaseEvent<KeyboardEvent<any>>) => {
if (keyboardNavigationBehavior === 'tab') {
if (e.target === ref.current
|| (e.target as HTMLElement).getAttribute('role') === 'gridcell'
|| (e.target as HTMLElement).getAttribute('role') === 'row'
|| e.key === 'Tab') {
originalOnKeyDownCapture?.(e);
}
}
if (keyboardNavigationBehavior !== 'tab') {
originalOnKeyDownCapture?.(e);
}
}, [originalOnKeyDownCapture, keyboardNavigationBehavior, ref]);
listProps.onKeyDownCapture = onKeyDownCapture;

let id = useId(props.id);
listMap.set(state, {id, onAction, linkBehavior, keyboardNavigationBehavior, shouldSelectOnPressUp});

Expand Down
58 changes: 41 additions & 17 deletions packages/@react-aria/gridlist/src/useGridListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/

import {chain, getScrollParent, mergeProps, scrollIntoViewport, useSlotId, useSyntheticLinkProps} from '@react-aria/utils';
import {chain, getScrollParent, isFocusable, mergeProps, scrollIntoViewport, useSlotId, useSyntheticLinkProps} from '@react-aria/utils';
import {DOMAttributes, FocusableElement, Key, RefObject, Node as RSNode} from '@react-types/shared';
import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus';
import {getRowId, listMap} from './utils';
Expand Down Expand Up @@ -135,6 +135,10 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
return;
}

if (keyboardNavigationBehavior === 'tab' && e.target !== ref.current) {
Copy link
Member

Choose a reason for hiding this comment

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

There's some logic below that previously ran on arrowup/arrowdown with keyboardNavigationBehavior = tab. Noticed in CardView you can no longer press up/down to move to the next card when focused on an action menu.

Copy link
Member Author

Choose a reason for hiding this comment

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

If I allow those through, it'll conflict with cursor positioning in textfields. I think we'll have to solve event leaks to have that not collide. We'll also need to make it non-capturing.

return;
}

let walker = getFocusableTreeWalker(ref.current);
walker.currentNode = document.activeElement;

Expand Down Expand Up @@ -227,22 +231,6 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
}
};

let onFocus = (e) => {
keyWhenFocused.current = node.key;
if (e.target !== ref.current) {
// useSelectableItem only handles setting the focused key when
// the focused element is the row itself. We also want to
// set the focused key when a child element receives focus.
// If focus is currently visible (e.g. the user is navigating with the keyboard),
// then skip this. We want to restore focus to the previously focused row
// in that case since the list should act like a single tab stop.
if (!isFocusVisible()) {
state.selectionManager.setFocusedKey(node.key);
}
return;
}
};

let onKeyDown = (e) => {
if (!e.currentTarget.contains(e.target as Element) || !ref.current || !document.activeElement) {
return;
Expand All @@ -265,6 +253,42 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
}
};

// Prevent pressing space to select the row.
let originalOnKeyDown = itemProps.onKeyDown;
itemProps.onKeyDown = (e) => {
if (keyboardNavigationBehavior === 'tab' && e.key === ' ' && e.target !== ref.current) {
e.stopPropagation();
return;
}
originalOnKeyDown?.(e);
};

// Prevent clicking on a focusable element from selecting the row.
let originalPointerDown = itemProps.onPointerDown;
itemProps.onPointerDown = (e) => {
if (keyboardNavigationBehavior === 'tab' && isFocusable(e.target as Element) && e.target !== ref.current) {
e.stopPropagation();
return;
}
originalPointerDown?.(e);
};

let onFocus = (e) => {
keyWhenFocused.current = node.key;
if (e.target !== ref.current) {
// useSelectableItem only handles setting the focused key when
// the focused element is the row itself. We also want to
// set the focused key when a child element receives focus.
// If focus is currently visible (e.g. the user is navigating with the keyboard),
// then skip this. We want to restore focus to the previously focused row
// in that case since the list should act like a single tab stop.
if (!isFocusVisible()) {
state.selectionManager.setFocusedKey(node.key);
}
Comment on lines +285 to +287
Copy link
Contributor

@nwidynski nwidynski Aug 8, 2025

Choose a reason for hiding this comment

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

Just flagging that this is still missing a fix to maintain a consistent tab sequence. Re-entering the collection from the bottom after exiting may cause focus to land on the item instead of the field. Same goes for the cell.onFocus().

For impl, see here

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I suspect this is related to #8686 as well
Thanks for the extra info, really appreciated. I'll make it much easier to determine if we tackle it in this PR as well.

return;
}
};

let syntheticLinkProps = useSyntheticLinkProps(node.props);
let linkProps = itemStates.hasAction ? syntheticLinkProps : {};
// TODO: re-add when we get translations and fix this for iOS VO
Expand Down
1 change: 0 additions & 1 deletion packages/@react-spectrum/list/test/ListViewDnd.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2425,7 +2425,6 @@ describe('ListView', function () {

tree.rerender(<DragBetweenListsComplex secondListDnDOptions={{...mockUtilityOptions, onRootDrop: null, onInsert: null}} />);
await user.tab({shift: true});
await user.tab({shift: true});
await beginDrag(tree);
await user.tab();
// Should automatically jump to the folder target since we didn't provide onRootDrop and onInsert
Expand Down
Loading