Repository navigation
fix(react-headless-components-preview): dismiss Popover on focus exit by default - #36929
Open
Paul Mardling (PaulGMardling) wants to merge 8 commits into
Conversation
…t dismissal Preserve destination focus when a non-modal Popover's logical focus boundary is exited. Keep the behavior opt-in and honor controlled state, native closing, and existing modal semantics. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44bec1f8-d9c2-4333-a285-7270b37fe5da
📊 Bundle size reportUnchanged fixtures
|
|
Pull request demo site: URL |
Make non-modal focus-exit dismissal built-in accessibility behavior rather than a consumer opt-in. Remove the proposed public API additions and cover default TeachingPopover and InfoLabel behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44bec1f8-d9c2-4333-a285-7270b37fe5da
…tion Preserve logical React portal focus through external shadow roots, retain native listeners across commits, and detect confirmed iframe focus exits without treating window blur alone as dismissal. Add unit and real-browser regressions while keeping the public API unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44bec1f8-d9c2-4333-a285-7270b37fe5da
…ship Classify the live focus destination after Portal layout ownership settles, observe both trigger and surface portal roots, and retain bounded frame observation with direct same-origin events. Preserve native close intent through concurrent content updates and add native-browser and cleanup regressions. Keep the public API unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44bec1f8-d9c2-4333-a285-7270b37fe5da
…root chains Observe every shadow ancestor for owned React focus, including nested closed roots. Cover whole-Popover and Fluent Portal nesting plus the approved bounded opaque-frame fallback without mixing InfoLabel changes into this branch. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44bec1f8-d9c2-4333-a285-7270b37fe5da
…ycle Preserve native-close focus ownership through content replacement, resolve owned focus across accessible iframe chains, and retire navigated document observers safely. Guard reentrant unmounts against stale observations and resource allocation while retaining cached-document subscriptions for Back/Forward restoration. Add native browser and unit regressions without changing the public API. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44bec1f8-d9c2-4333-a285-7270b37fe5da
Paul Mardling (PaulGMardling)
marked this pull request as ready for review
October 20, 2026 16:00
Keep master's released dependency versions and align the test-only Portal dependency with version 9.8.17. Preserve the existing Popover focus engine and public API reports. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44bec1f8-d9c2-4333-a285-7270b37fe5da
| @@ -0,0 +1,6 @@ | |||
| { | |||
There was a problem hiding this comment.
🕵🏾♀️ visual changes to review in the Visual Change Report
vr-tests-react-components/Menu Converged - submenuIndicator slotted content 2 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default - RTL.submenus open.chromium.png | 404 | Changed |
| vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default.submenus open.chromium.png | 605 | Changed |
vr-tests-react-components/Positioning 2 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-react-components/Positioning.Positioning end.chromium.png | 505 | Changed |
| vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png | 624 | Changed |
vr-tests-react-components/ProgressBar converged 2 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png | 50 | Changed |
| vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png | 54 | Changed |
vr-tests-react-components/Skeleton converged 2 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-react-components/Skeleton converged.Opaque Skeleton with rectangle - Dark Mode.default.chromium.png | 10 | Changed |
| vr-tests-react-components/Skeleton converged.Opaque Skeleton with square - Dark Mode.default.chromium.png | 2 | Changed |
vr-tests-react-components/TagPicker 1 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-react-components/TagPicker.disabled - RTL.disabled input hover.chromium.png | 635 | Changed |
vr-tests-web-components/Avatar 1 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-web-components/Avatar. - Dark Mode.normal.chromium.png | 10380 | Changed |
vr-tests-web-components/Badge 1 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-web-components/Badge. - Dark Mode.normal.chromium.png | 443 | Changed |
vr-tests-web-components/MenuList 3 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-web-components/MenuList. - RTL.1st selected.chromium_2.png | 39384 | Changed |
| vr-tests-web-components/MenuList. - RTL.2nd selected.chromium_3.png | 38816 | Changed |
| vr-tests-web-components/MenuList. - RTL.normal.chromium_1.png | 39083 | Changed |
vr-tests-web-components/TextInput 1 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests-web-components/TextInput. - Dark Mode.normal.chromium_1.png | 288 | Changed |
vr-tests/Callout 3 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests/Callout.Gap space 25.default.chromium.png | 2195 | Changed |
| vr-tests/Callout.Top right edge.default.chromium.png | 1146 | Changed |
| vr-tests/Callout.Bottom left edge.default.chromium.png | 2195 | Changed |
vr-tests/react-charting-LineChart 2 screenshots
| Image Name | Diff(in Pixels) | Image Type |
|---|---|---|
| vr-tests/react-charting-LineChart.Multiple - RTL.default.chromium.png | 200 | Changed |
| vr-tests/react-charting-LineChart.Multiple.default.chromium.png | 192 | Changed |
There were 2 duplicate changes discarded. Check the build logs for more information.
Normalize the test-only Portal dependency, close native state before releasing the surface in legacy React roots, and compact private observation state with one scoped frame sampler. Preserve the existing public API and 10% bundle budget. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 44bec1f8-d9c2-4333-a285-7270b37fe5da
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
Non-modal headless Popover stays open after Tab moves focus outside. Enabling
trapFocusprevents reaching the next page control instead of fixing dismissal.New Behavior
Automatically dismiss non-modal Popover when focus leaves the trigger/surface logical boundary, preserving destination focus and controlled accept/reject behavior. No public API additions; modal behavior is unchanged.
Cross-origin frame transitions use one scoped 100 ms sampler per Popover. An entire round trip between samples can be missed; instantaneous/universal detection is not guaranteed. InfoLabel/InfoButton fixes are separate work.
Validation:
yarn nx run react-headless-components-preview:test --runInBand— 1,305 tests passed. Full React 17 and 18 unit runs also passed (1,305 each); native browser regressions passed in Chrome and both compatibility browsers.yarn nx run react-headless-components-preview:bundle-size --excludeTaskDependencies --skip-nx-cache— measured TeachingPopover at 37,107 bytes minified versus the CI baseline's 33,739. The unchanged 10% minified-size gate passes (9.98% growth); gzip is 12,445 versus 11,083 bytes.yarn nx generate @fluentui/workspace-plugin:normalize-package-dependencies --verify— passed.yarn nx run react-headless-components-preview:type-check --excludeTaskDependenciesandyarn nx run react-headless-components-preview:lint— passed; public API reports unchanged.Fixes for all three previous CI failures are pushed in
92e88b4e9e; hosted checks must rerun. Windows Edge evidence covers earlier head9c5cd5993c, not these latest repairs. Latest-head Windows/Narrator and TCS verification remain outstanding.Related Issue(s)