Focus system hardening: traversal, zones, traps, layers, and persistence#81
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d50a256c9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (firstInTrap !== undefined && focusSet.has(firstInTrap)) { | ||
| nextFocusedId = firstInTrap; | ||
| const trapFocusables = trap.focusableIds; | ||
| if (trapFocusables.length > 0) { |
There was a problem hiding this comment.
Honor trap initialFocus when trap focusables list is empty
This new guard makes initialFocus unreachable whenever trap.focusableIds is empty, but traps can legitimately have an empty direct list when their focusable descendants are wrapped in nested containers (for example a focusZone, per widgetMeta’s innermost-container attribution). In that case, activating a trap with a valid initialFocus inside the trap no longer moves focus into the modal and leaves focus outside while the trap is active, which is a regression from the prior behavior where initialFocus could still be applied.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in 8afacab. initialFocus is now honored when a trap has an empty direct focusableIds list (e.g. nested focus containers), while still enforcing containment when direct trap focusables are present. Added regressions in focus.traps.test.ts for both metadata-level and integration cases.
Summary
tabIndextiesinitialFocusto IDs inside the trap with deterministic fallbackselectedIndexis non-selectableTests Added
packages/core/src/runtime/__tests__/focus.traversal.test.ts(31)packages/core/src/runtime/__tests__/focus.zones.test.ts(33)packages/core/src/runtime/__tests__/focus.traps.test.ts(18)packages/core/src/runtime/__tests__/focus.layers.test.ts(23)packages/core/src/runtime/__tests__/focus.persistence.test.ts(14)Docs
docs/guide/input-and-focus.mdwith persistence rules, zone tie-ordering, and trapinitialFocusfallback guidanceValidation
npm run lintnpm run buildnpm run typechecknode scripts/run-tests.mjs