Skip to content

fix(react-aria): suppress focus ring updates during Shift+Tab collection pivot#10283

Open
coolassassin wants to merge 1 commit into
adobe:mainfrom
coolassassin:shift-tab-in-table-fix-alternative
Open

fix(react-aria): suppress focus ring updates during Shift+Tab collection pivot#10283
coolassassin wants to merge 1 commit into
adobe:mainfrom
coolassassin:shift-tab-in-table-fix-alternative

Conversation

@coolassassin

Copy link
Copy Markdown

Summary

When Shift+Tab is pressed inside a collection with allowsTabNavigation={false} (Table, ListBox, GridList, etc.), useSelectableCollection calls ref.current.focus() on the collection root so the browser can continue tabbing from the collection boundary. That brief focus on the grid root is intentional, but it caused extra focus transitions and downstream React re-renders (for example Row useFocusRing({ within: true }) when focus briefly lands on the grid root).

Closes #10254.

This PR keeps the existing pivot (collectionRoot.focus() + browser default tab order) and suppresses transient focus ring / focus-visible React state updates while focus moves from an internal tab stop to the collection root during Shift+Tab exit. No manual tab-stop walking; shadow DOM / iframe behavior is unchanged.

Behavior:

  • Shift+Tab still pivots through the collection root; tab order is not managed manually.
  • During the pivot, useFocus / useFocusWithin skip onFocusChange when focus moves descendant → collection root.
  • Final exit (collection root → external tab stop) is not suppressed; setFocused(false) on collection blur is unchanged.
  • ignoreFocusEvent is also set during the pivot so global keyboard focus-visible modality is not affected.

✅ Pull Request Checklist:

  • Connected with this 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). — N/A, behavior-only fix.
  • Looked at the Accessibility Practices for this feature - Aria Practices — Grid and Table Keyboard Interaction: Tab / Shift+Tab should move focus into/out of the grid without trapping on the grid container.

📝 Test Instructions:

Automated

yarn jest packages/react-aria/test/interactions/transientFocus.test.js
yarn jest packages/react-aria/test/interactions/useFocusWithin.test.js -t "pivoting|transient|re-render"
yarn jest packages/react-aria/test/selection/useSelectableCollection.test.js -t "collection root"
yarn jest packages/@adobe/react-spectrum/test/table/Table.test.js -t "should move focus before the table when shift tabbing"
yarn jest packages/@adobe/react-spectrum/test/combobox/ComboBox.test.js -t "tab and shift tab"
yarn jest packages/@adobe/react-spectrum/test/menu/SubMenuTrigger.test.tsx
yarn jest packages/react-aria-components/test/Tree.test.tsx -t "should focus the dropped row when dropped on a parent that is expanded"
yarn jest packages/@adobe/react-spectrum/test/menu/MenuTrigger.test.js -t "shift tabbing"

The new useSelectableCollection test uses fireEvent.keyDown (not user.tab({ shift: true })) so it exercises useSelectableCollection's Tab handler — same caveat as GridList.test.js.

Manual (Table / Storybook)

  1. Place a focusable element before a Table (e.g. a button).
  2. Tab into the table, focus a cell or an internal control (switch, button).
  3. Shift+Tab back to the button.
  4. Confirm focus lands on the button (same as before).
  5. Optional: in React DevTools Profiler, confirm fewer cell re-renders on Shift+Tab vs. before — especially rows/cells that are not focused.

Regression

  • Tab into a collection still works.
  • Tab out (forward) is unchanged.
  • Shift+Tab between items inside a collection still works.

🧢 Your Project:

dxFeed (Devexperts) — internal Table built on react-aria-components (Table, div-grid, focusMode="cell").

…ion pivot

Avoid unnecessary re-renders when exiting a collection via Shift+Tab by
suppressing transient focus ring state while focus briefly moves to the
collection root. Browser tab order is unchanged; only React focus hooks
skip updates during the pivot.

Co-authored-by: Cursor <cursoragent@cursor.com>

� Conflicts:
�	packages/react-aria/src/selection/useSelectableCollection.ts
�	packages/react-aria/test/selection/useSelectableCollection.test.js
@coolassassin coolassassin force-pushed the shift-tab-in-table-fix-alternative branch from 638f9bf to 3cff724 Compare July 1, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Table] Multiple renders on Shift+Tab

1 participant