Skip to content

fix: Escape in an open Menu does not trigger tabster actions#36313

Open
layershifter wants to merge 2 commits into
microsoft:masterfrom
layershifter:fix/react-menu-escape-tabster-groupper
Open

fix: Escape in an open Menu does not trigger tabster actions#36313
layershifter wants to merge 2 commits into
microsoft:masterfrom
layershifter:fix/react-menu-escape-tabster-groupper

Conversation

@layershifter

Copy link
Copy Markdown
Member

Previous Behavior

A Menu nested in a tabster groupper/modalizer (e.g. a Card with focusMode, or a reaction popover that shares a modalizer id with a parent message groupper) moves focus to the groupper root when Escape closes the menu, instead of restoring focus to the trigger.

The MenuPopover keydown handler calls event.preventDefault() (and the comment intends to stop propagation), but tabster listens for keydown in the capture phase on the document, so neither prevents tabster's own Escape behaviour from escaping the parent groupper.

New Behavior

Adds the tabster focusable.ignoreKeydown: { Escape } attribute on the MenuPopover, gated on the menu being open — so tabster ignores Escape while the menu handles it to close, and Escape still functions normally when the menu is closed.

This is the same fix applied to react-combobox in #36275 (useTriggerSlot). It lives on the MenuPopover (rather than the trigger) because for a menu, focus moves into the menu items, and tabster aggregates ignoreKeydown from the focused element's ancestors.

Related Issue(s)

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-charts
AreaChart
403.979 kB
125.912 kB
404.322 kB
126.056 kB
343 B
144 B
react-charts
DeclarativeChart
754.98 kB
220.248 kB
755.323 kB
220.437 kB
343 B
189 B
react-charts
DonutChart
314.442 kB
96.656 kB
314.785 kB
96.812 kB
343 B
156 B
react-charts
FunnelChart
305.941 kB
93.468 kB
306.284 kB
93.624 kB
343 B
156 B
react-charts
GanttChart
387.079 kB
120.263 kB
387.422 kB
120.42 kB
343 B
157 B
react-charts
GaugeChart
313.82 kB
96.023 kB
314.163 kB
96.187 kB
343 B
164 B
react-charts
GroupedVerticalBarChart
394.95 kB
122.987 kB
395.293 kB
123.143 kB
343 B
156 B
react-charts
HeatMapChart
389.155 kB
121.302 kB
389.498 kB
121.461 kB
343 B
159 B
react-charts
HorizontalBarChart
294.114 kB
89.141 kB
294.457 kB
89.29 kB
343 B
149 B
react-charts
Legends
233.794 kB
69.977 kB
234.137 kB
70.117 kB
343 B
140 B
react-charts
LineChart
415.309 kB
128.873 kB
415.652 kB
129.018 kB
343 B
145 B
react-charts
PolarChart
342.793 kB
106.526 kB
343.136 kB
106.676 kB
343 B
150 B
react-charts
ScatterChart
394.692 kB
122.992 kB
395.035 kB
123.149 kB
343 B
157 B
react-charts
VerticalBarChart
431.43 kB
127.922 kB
431.773 kB
128.084 kB
343 B
162 B
react-charts
VerticalStackedBarChart
401.154 kB
124.425 kB
401.497 kB
124.578 kB
343 B
153 B
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
226.19 kB
67.909 kB
226.531 kB
68.074 kB
341 B
165 B
react-components
react-components: entire library
1.296 MB
325.498 kB
1.296 MB
325.535 kB
56 B
37 B
react-menu
Menu (including children components)
159.755 kB
50.626 kB
160.102 kB
50.764 kB
347 B
138 B
react-menu
Menu (including selectable components)
162.933 kB
51.253 kB
163.28 kB
51.395 kB
347 B
142 B
Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-avatar
Avatar
47.215 kB
14.859 kB
react-avatar
AvatarGroup
16.316 kB
6.528 kB
react-avatar
AvatarGroupItem
60.361 kB
18.763 kB
react-breadcrumb
@fluentui/react-breadcrumb - package
102.926 kB
28.76 kB
react-charts
HorizontalBarChartWithAxis
63 B
83 B
react-charts
SankeyChart
211.914 kB
67.836 kB
react-charts
Sparkline
80.503 kB
26.644 kB
react-components
react-components: Button, FluentProvider & webLightTheme
66.328 kB
19.02 kB
react-components
react-components: FluentProvider & webLightTheme
39.525 kB
13.113 kB
react-datepicker-compat
DatePicker Compat
216.454 kB
63.95 kB
react-dialog
Dialog (including children components)
90.204 kB
27.895 kB
react-headless-components-preview
react-headless-components-preview: entire library
236.96 kB
67.865 kB
react-overflow
hooks only
9.117 kB
3.241 kB
react-persona
Persona
54.17 kB
16.793 kB
react-popover
Popover
125.741 kB
40.448 kB
react-portal-compat
PortalCompatProvider
5.567 kB
2.237 kB
react-table
DataGrid
147.115 kB
43.627 kB
react-table
Table (Primitives only)
36.931 kB
12.324 kB
react-table
Table as DataGrid
118.7 kB
33.35 kB
react-table
Table (Selection only)
65.492 kB
18.622 kB
react-table
Table (Sort only)
64.135 kB
18.232 kB
react-tag-picker
@fluentui/react-tag-picker - package
174.152 kB
54.127 kB
react-tags
InteractionTag
12.464 kB
4.95 kB
react-tags
Tag
28.389 kB
8.911 kB
react-tags
TagGroup
69.754 kB
21.41 kB
react-teaching-popover
TeachingPopover
101.1 kB
31.859 kB
react-timepicker-compat
TimePicker
141.076 kB
46.036 kB
react-tree
FlatTree
135.901 kB
40.452 kB
react-tree
PersonaFlatTree
137.729 kB
40.967 kB
react-tree
PersonaTree
133.79 kB
39.741 kB
react-tree
Tree
131.968 kB
39.256 kB
🤖 This report was generated against b2ded7348765a36d6804033a49c54fc30ca3757c

@github-actions

Copy link
Copy Markdown

Pull request demo site: URL

A Menu nested in a tabster groupper/modalizer (e.g. a focusMode Card, or a
list item whose actions popover shares a modalizer scope with the item) moves
focus to the groupper root when Escape closes the menu, instead of restoring
focus to the trigger.

The MenuPopover keydown handler calls event.preventDefault(), but tabster
listens for keydown in the capture phase on the document, so it can't be
stopped from the popover handler and still runs its own Escape behaviour
(escaping the parent groupper). This is the same problem fixed for
react-combobox in microsoft#36275.

Fix: add the tabster `focusable.ignoreKeydown: { Escape: true }` attribute on
the MenuPopover. The popover only renders while the menu is open, so the
attribute is only ever present on an open menu (and Escape still works normally
elsewhere) — no need to gate it on the open state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@layershifter layershifter force-pushed the fix/react-menu-escape-tabster-groupper branch from 6e4657c to 33c79ca Compare June 16, 2026 10:20
@@ -0,0 +1,7 @@
{

@github-actions github-actions Bot Jun 16, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🕵🏾‍♀️ 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 413 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 151 Changed
vr-tests-react-components/Positioning.Positioning end.chromium.png 279 Changed
vr-tests-react-components/ProgressBar converged 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 67 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 115 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 117 Changed
vr-tests-react-components/TagPicker 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled.disabled input hover.chromium.png 677 Changed

There were 1 duplicate changes discarded. Check the build logs for more information.

@layershifter layershifter marked this pull request as ready for review June 16, 2026 12:44
@layershifter layershifter requested a review from a team as a code owner June 16, 2026 12:44
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.

1 participant