Repository navigation
feat(react-menu): add opt-in autofocus suppression - #36864
Open
Paul Mardling (PaulGMardling) wants to merge 13 commits into
Open
Paul Mardling (PaulGMardling) wants to merge 13 commits into
Paul Mardling (PaulGMardling) wants to merge 13 commits into
Conversation
Add experimental opt-in Menu props for opening autofocus suppression and external trigger registration. Preserve trigger ownership across lifecycle transitions and restore focus only on close. Include regression coverage, editable-trigger guidance, API report, and minor release metadata. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
📊 Bundle size reportUnchanged fixtures
|
|
Pull request demo site: URL |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The autofocus flag can refocus an open menu when its effective value has not changed, and the public child-shape documentation is inconsistent.
2 open findings
What changed in this PR
Adds experimental Menu support for external editable triggers and optional autofocus suppression.
Changes:
- Adds external trigger registration and autofocus control.
- Adds lifecycle, focus, navigation, and containment tests.
- Adds documentation, API report, story, and change file.
| File | Description |
|---|---|
MenuExternalEditableTrigger.stories.tsx |
Demonstrates editable external-trigger composition. |
MenuBestPractices.md |
Documents experimental usage guidance. |
index.stories.tsx |
Exports the new story. |
useMenu.tsx |
Implements trigger registration and autofocus suppression. |
useMenu.test.tsx |
Tests external registration lifecycle. |
Menu.types.ts |
Adds the experimental public props. |
Menu.test.tsx |
Tests composition transitions and Strict Mode. |
Menu.cy.tsx |
Covers focus, navigation, dismissal, and positioning. |
react-menu.api.md |
Updates the generated API report. |
| Change file | Records a minor react-menu feature release. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Normalize omitted autofocus suppression to false, cover transitions in both directions, and document external trigger single-child composition. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
Demonstrate query-bound asynchronous filtering, explicit focus entry, supported textbox ARIA, and native menu Tab behavior. Keep unsupported composition props out of the separate headless Menu contract. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
Place the persistent live status above the editable trigger so command results do not cover its text. Cover the visible status bounds with a native browser regression. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
Remove external trigger registration and its editable-search composition. Retain the single experimental autofocus flag needed by the WXP patch, with focused examples, regression coverage, and the independent headless API boundary. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
Assert that the styled Menu autofocus prop is absent from the headless component's public props without constructing invalid JSX or suppressing a TypeScript diagnostic. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
Keep the explicit Menu prop exclusion and generated API contract without wrapping a compile-time key assertion in a tautological runtime test. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
…ession Share the normalized autofocus policy through Menu state and context, and honor it in native popover focus handling. Preserve default opening focus, caller-authored attributes, and nested-menu behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
…navigation Synchronize accepted native Menu closure in a layout effect so React 17/Electron 118 does not lose focus to body before the popover hides. Keep opening autofocus in its existing passive effect. Cover root and nested Tab/Shift+Tab with default and suppressed autofocus, controlled close refusal, and duplicate close notifications. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f91ed3f-5fa6-4307-b505-a6ac353feb16
Paul Mardling (PaulGMardling)
marked this pull request as ready for review
October 16, 2026 08:36
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Previous Behavior
The WXP Ribbon Search/TellMe migration from v8
ContextualMenuneeds command results to open without taking focus from its editable input. Users must be able to continue typing and selecting text until the Ribbon explicitly moves focus into the results.V9 Menu automatically focuses its first item when opened, so the WXP migration currently needs a dependency patch to suppress that behavior. Headless Menu also supplies automatic opening focus through its native popover implementation.
New Behavior
Adds one opt-in experimental Menu prop,
unstable_disableAutoFocus, matching the capability needed by the WXP patch. It defaults tofalse; when enabled, opening the menu does not automatically focus its first item. The same prop works in both styled v9 Menu and headless Menu, with actual native-popover focus handling rather than type-only support. Nested menus retain their own defaults.This PR is limited to autofocus suppression, not external-trigger registration or a complete editable-search widget. A focused styled API example explicitly implements keyboard entry. Browser regressions cover continued typing and text selection while externally controlled results open in both implementations.
Existing opening-focus defaults and close-focus restoration are preserved. Equivalent omitted/
falsevalues do not reset the focused item, and changing the flag while closed does not move focus. Turning suppression off while open focuses the first item. Headless Menu cleans up its own autofocus attribute without removing consumer-authored attributes.The expanded keyboard coverage reproduced a forward-Tab failure on React 17/Electron 118 with both default and suppressed autofocus: the native surface remained open through the browser's default Tab action, and focus ended on the document body. Accepted headless closes now hide the surface during layout, before native Tab navigation. Opening focus remains in its existing effect; controlled owners can still decline closing. Regressions cover root/nested Tab and Shift+Tab, controlled close refusal, and duplicate close notifications.
Menu remains appropriate for the Ribbon's executable command hierarchy; this is not a drop-in v8
ContextualMenureplacement. Keyboard entry, asynchronous results, host-specific restoration, and actual Ribbon/screen-reader validation remain consumer responsibilities. Shared interaction behavior is available to headless consumers without promising identical styled/design APIs or verified Fluent Modern integration.Validation:
yarn nx run react-menu:test --runInBand— 494 passed; all 16 snapshots passed.yarn nx run react-headless-components-preview:test --runInBand --runTestsByPath src/components/Menu/Menu.test.tsx --skipNxCache— 23 passed.yarn nx run react-menu:e2e --spec src/components/Menu/Menu.cy.tsx --config retries=0 --browser chrome --skipNxCache— 83 passed.yarn nx run react-headless-components-preview:e2e --spec src/components/Menu/Menu.cy.tsx --config retries=0 --browser chrome --skipNxCache— 30 passed.yarn nx run react-headless-components-preview:test-rit--17--e2e --skipNxCache— all 13 specs passed on React 17/Electron 118: 258 passed, 9 pending; all 30 Menu tests passed.Related Issue(s)