Repository navigation
docs(spec): DropdownMenu records its shipped contract as current - #9113
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
PR Analysis Report
Generated by PR Enrichment workflow | View current CI run |
There was a problem hiding this comment.
This spec backfill can't be approved yet: these eight records would make DropdownMenu requirements current, which needs an engineering owner's decision on this exact head. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Approving also settles the pending link-row and sub-menu header/footer decisions that become FR10 and FR20, and the mouse press-open that the useMenuPress module record still lists as proposed. Whatever the decision, please fix these record errors:
DropdownMenu.spec.mdFR8 says touch opening follows AST-094 FR29, but the trigger still waits the released 500 ms hold, so it should be written as an implementation gap.- FR10, ORD1 and
DropdownMenuItemFR3 leave out that a modified click on a link row still closes the menu. DropdownMenuCheckboxItemkeeps the menu open on Enter without naming the exception AST-020 FR15 asks for, and its ORD1 puts the close afteronClick, though the code closes first.
[Automated review]
| | FR5 | Both menu surfaces carry `data-astryx-menu-press` and follow `module:DropdownMenu/useMenuPress` FR1–FR7, FR10, and FR11: the row under the release acts, the highlight follows a held pointer, a mouse released outside closes and a finger leaves the menu open, the browser's stray click never acts, and the Pointer menu surface declares `touch-action` by overflow. The grow, collapse, and pan dismissal under a finger are FR21. | `module:DropdownMenu/useMenuPress`; `spec:AST-094` FR26–FR28 | | ||
| | FR6 | When the pointer menu closes, focus returns to the Trigger button with a visible ring only when the menu was driven by keyboard; a press outside that landed on a focusable control keeps focus there. | `architecture:interaction-modality` INV1; DEC-1 | | ||
| | FR7 | `menuMaxHeight` replaces the 300px term of the menu's and its popover viewport's block-size cap while the viewport gutters still bound both; the touch sheet ignores it. | DEC-2 | | ||
| | FR8 | The Trigger button opens the pointer menu on a mouse press-down, with the module's settle rule on the opening release, and under a finger as `spec:AST-094` FR29 states; pressing the trigger of an open menu closes it without reopening in the same gesture; a mouse click and the keyboard open as before. | `module:DropdownMenu/useMenuPress` FR8, FR9 | |
There was a problem hiding this comment.
The touch half of FR8 isn't shipped. The trigger still opens on the released 500 ms long press (MENU_PRESS_LONG_PRESS_MS = 500 in hooks/useMenuPress.ts, and DropdownMenu.tsx passes no longPressDelayMs), and the cited case "a held touch on the trigger opens with the finger down…" asserts exactly that delay. AST-094 FR29 asks for an open as the finger presses in a fixed bar. Could this become "MUST … Implementation gap" with a Current-state impact entry, as FR21 is written? The mouse half also cites useMenuPress FR8, which that module still lists as proposed.
|
|
||
| ### Transformation and precedence order | ||
|
|
||
| - **ORD2 — Link row activation.** modified click → the browser alone; else | ||
| - **ORD1 — Link row activation.** modified click → the browser alone; else |
There was a problem hiding this comment.
"modified click → the browser alone" doesn't match the source. DropdownMenuItem.tsx skips onClick on a modified click but still calls closeMenu() ("The menu still closes: the row acted."), and the touch sheet does the same. Suggest: modified click → no onClick, close (hasCloseOnSelect), the browser opens it. FR10 and DropdownMenuItem FR3 need the same clause.
| | ID | Invariant | Basis | | ||
| | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | ||
| | FR1 | The row renders one element with `role="menuitemcheckbox"` whose `aria-checked` equals `value`; the checkbox marker is decorative (`aria-hidden`), so the row is the only announced control. | `spec:AST-020` FR15; current source and tests | | ||
| | FR2 | An enabled activation, by pointer or by the parent's synthesized Enter/Space click, calls `onChange(!value)` and the caller's `onClick`; the menu stays open unless `hasCloseOnSelect` is set, in which case it closes after the toggle. | `architecture:public-component-api` INV7; current tests | |
There was a problem hiding this comment.
Enter on this row keeps the menu open by default. The adopted Menu pattern's Enter closes it, and __tests__/Menu.a11y.known-failures.ts records menu.keys.enter-activates-and-closes failing on this row. AST-020 FR15 asks the owning record to name a required part it does not adopt, with the reason. Could this record add that exception (a DEC), or list it as a gap with an open question?
|
|
||
| ### Transformation and precedence order | ||
|
|
||
| - **ORD1 — Activation.** Disabled guard → `onChange(!value)` and caller |
There was a problem hiding this comment.
The source's order is onChange(!value), then closeMenu() when hasCloseOnSelect, then the caller's onClick (DropdownMenuCheckboxItem.tsx:176-188), the same order RadioItem FR3 states. Suggest: Disabled guard → onChange(!value) → close when hasCloseOnSelect → caller onClick.
There was a problem hiding this comment.
These eight DropdownMenu records can't be approved yet: making them current settles requirements no engineering owner has ruled on at this head, including the pending link-row and sub-menu header/footer decisions (now FR10 and FR20) and the safe triangle (FR13). An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Thanks for fixing the earlier record errors; whatever the decision, three fixes remain:
DropdownMenu.spec.mdFR8 requires opening as the finger presses everywhere, but AST-094 FR29 asks for that only in a fixed bar, with a 150 ms wait inside a scroll view and the tap-open kept underpresentation="popover".- DEC-7's decider line is DEC-2's, carried over when the safe-triangle heading was inserted above DEC-2's body, so FR13 reads as owner-decided.
- Two verification rows quote
aria-_, data-_where the test names sayaria-*, data-*.
[Automated review]
| | FR5 | Both menu surfaces carry `data-astryx-menu-press` and follow `module:DropdownMenu/useMenuPress` FR1–FR7, FR10, and FR11: the row under the release acts, the highlight follows a held pointer, a mouse released outside closes and a finger leaves the menu open, the browser's stray click never acts, and the Pointer menu surface declares `touch-action` by overflow. The grow, collapse, and pan dismissal under a finger are FR21. | `module:DropdownMenu/useMenuPress`; `spec:AST-094` FR26–FR28 | | ||
| | FR6 | When the pointer menu closes, focus returns to the Trigger button with a visible ring only when the menu was driven by keyboard; a press outside that landed on a focusable control keeps focus there. | `architecture:interaction-modality` INV1; DEC-1 | | ||
| | FR7 | `menuMaxHeight` replaces the 300px term of the menu's and its popover viewport's block-size cap while the viewport gutters still bound both; the touch sheet ignores it. | DEC-2 | | ||
| | FR8 | The Trigger button opens the pointer menu on a mouse press-down, with a settle rule on the opening release, and pressing the trigger of an open menu closes it without reopening in the same gesture; a mouse click and the keyboard open as before. The mouse press-open is shipped behavior, not a requirement until `module:DropdownMenu/useMenuPress` DEC-4 is ruled. Under a finger the trigger MUST open the menu as the finger presses (`spec:AST-094` FR29). Implementation gap: a held finger opens it after a 500 ms hold. | `module:DropdownMenu/useMenuPress` FR8, FR9; `spec:AST-094` FR29 | |
There was a problem hiding this comment.
The touch half is wider than AST-094 FR29: FR29 opens on press only in a fixed bar, waits the 150 ms content-touch delay (or opens at a tap's lift) inside a scroll view, and keeps the tap-open under presentation="popover". As written, a change that opens on press inside a scroll view, or under a pinned popover, would conform to FR8 while failing FR29. Suggest: "Under a finger the trigger MUST open the menu as spec:AST-094 FR29 states: as the finger presses in a fixed bar, after the 150 ms content-touch delay or at a tap's lift inside a scroll view; presentation="popover" keeps the tap open." The End-user impact row (line 45, "opens only after a held press") also misses that a tap opens at its click today.
| @@ -499,19 +499,7 @@ that and a case pins the behavior instead. | |||
| **Reference:** `component:DropdownMenu/DEC-7` | |||
| **Decider:** `cixzhang`, `2026-10-02` | |||
There was a problem hiding this comment.
This decider line came with DEC-2: the safe-triangle heading was inserted between DEC-2's heading and its Reference/Decider/body, and the PR restores the same line under DEC-2 (line 390). main records FR13 as "Proposed … pending owner review", so keeping it here makes FR13 read as decided on a date nobody recorded for it. Could DEC-7 drop the decider line (FR13 basis "Current behavior", as FR10 and FR20 do), or name who actually ruled?
| | FR3 | `DropdownMenu.test.tsx` "DropdownMenuItem href" and "link rows — the browser's own clicks" cases; "a middle click on a link row runs the caller's onAuxClick and still closes the menu" | Plain, modified, middle, right click; blocked destination | A non-anchor row, a modified click that runs the callback, a middle click that does not close, or a live blocked destination fails. | `audit:DropdownMenuItem/behavior` | | ||
| | FR4, AR3 | `DropdownMenu.test.tsx` keyboard-access cases ("moves focus to the item the mouse hovers, keeping a single highlight", "does not move focus on hover for a disabled item", "does not move focus for a non-mouse (touch) pointer") and "keeps the row's own role, tab stop and hover focus beside a caller's onPointerMove" | Mouse hover, touch, disabled | A second highlight, hover focus on a disabled row or under touch, or a lost tab stop fails. | `audit:DropdownMenuItem/accessibility` | | ||
| | FR5 | `DropdownMenu.test.tsx` destructive-variant cases; `themingTargets.test.ts` | Default and destructive; data and compound mode | A missing `data-variant` fails; a rendered class absent from the documented targets fails. Evidence gap: no test asserts the `dropdown-menu-item` class or `data-size`. | `audit:DropdownMenuItem/theming` | | ||
| | FR6 | `DropdownMenu.test.tsx` ref and host-attribute cases ("a ref reaches the menuitem element", "puts aria-_, data-_ and id on the row root, a link row included", "runs the DOM handlers the caller attaches, such as a drop target") | Ref; host attributes; caller handlers | A dropped ref, attribute, or handler fails. Evidence gap: no test asserts `className`, `style`, or `xstyle` on the row. | `audit:DropdownMenuItem/api` | |
There was a problem hiding this comment.
The test is named "puts aria-, data- and id on the row root, a link row included" (DropdownMenu.test.tsx:1538); Prettier turned the asterisks into _. Escaping them (aria-\*, data-\*) keeps the quoted name searchable.
| | FR4 | `DropdownMenuSubMenu.test.tsx` drill-in cases; `DropdownMenuDrillIn.a11y.chromium.spec.ts` | Drilled view; Back | A missing Back row or a Back that does not return fails. | `audit:DropdownMenuSubMenu/behavior` | | ||
| | FR5 | `DropdownMenuSubMenu.test.tsx` "closes the whole menu after selecting a nested item" and "activates a nested item with the Enter key and closes the menu" | Pointer and Enter | A level left open after a leaf pick fails. | `audit:DropdownMenuSubMenu/behavior` | | ||
| | FR6 | `DropdownMenuSubMenu.test.tsx` "reports each flyout open and close to onOpenChange once" | Flyout open and close | A missing or repeated report fails. Evidence gap: the drill-in reset gap is not asserted. | `audit:DropdownMenuSubMenu/behavior` | | ||
| | FR7, FR9 | `DropdownMenuSubMenu.test.tsx` "does not open a disabled submenu", "a disabled row does not pass a click on to the caller's onClick", host-attribute and ref cases ("aria-_, data-_ and id reach the trigger row, and the flyout stays labelled by it", "a ref reaches the trigger row", "a caller's handlers run after the row's own, and the row still opens and takes the hover focus") | Disabled; caller handlers and attributes | A disabled open or caller click, or a dropped attribute, ref, or handler, fails. Evidence gap: no test asserts `className`, `style`, or `xstyle` on the row. | `audit:DropdownMenuSubMenu/api` | |
There was a problem hiding this comment.
Same here: the case is "aria-, data- and id reach the trigger row, and the flyout stays labelled by it" (DropdownMenuSubMenu.test.tsx:427).
There was a problem hiding this comment.
These eight DropdownMenu records can't be approved yet: making them current settles requirements no engineering owner has ruled on at this head, including the pending link-row and sub-menu header/footer decisions (now FR10 and FR20), the safe triangle (FR13) and row focus visibility in forced colors. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Thanks for fixing the earlier FR8, DEC-7 and test-name issues; two record fixes remain whatever the decision:
DropdownMenu.spec.mdstill says "Released default preserved: yes" and "Migration decision: none", but FR8 and FR21 move the finger default that AST-094 FR25 lists as moved, withpresentation="popover"as the way back.- The FR20 verification row calls the ContextMenu rows-alone case an evidence gap, but
ContextMenu.test.tsxhas "shows a sub-menu with a header or footer as its rows alone, and says why in development".
[Automated review]
| - Released default preserved: yes | ||
| - Compatibility class: records shipped behavior; no public prop, default, | ||
| export, or DOM change | ||
| - Controlled/uncontrolled behavior: `isMenuOpen` with `onOpenChange`, or | ||
| uncontrolled | ||
| - Migration decision: none; a caller that needs the released tap-open under a | ||
| finger passes `presentation="popover"` (`spec:AST-094/DEC-4`) |
There was a problem hiding this comment.
FR8 and FR21 make the omitted presentation open on press and grow from the trigger under a finger, which AST-094 FR25 lists as a moved default ("DropdownMenu's omitted presentation, released as 'popover', which presentation="popover" keeps"), and useMenuPress.spec.md records the same timing move as "Released default preserved: no". Suggest: "Released default preserved: no under a finger: the omitted presentation follows spec:AST-094 FR29, FR31 and FR32 (FR8, FR21); presentation="popover" keeps the released behavior (spec:AST-094 FR25)" and "Migration decision: spec:AST-094 FR25 and DEC-4".
| | FR14 | `DropdownMenu.test.tsx` section cases | Titled and untitled sections | A missing group role or title fails. | `audit:DropdownMenu/accessibility` | | ||
| | FR15, AR5 | `DropdownMenu.test.tsx` header and footer cases, including the hidden-control, open-sub-menu, and header radio-group cases; `MenuHeaderFooter.a11y.chromium.spec.ts` | Regions present and absent; hidden controls; open flyout; custom trigger; bottom sheet | A region inside the menu, a missing or unnamed dialog, a wrong `aria-haspopup`, a Tab that leaves the popover, a hidden control taken as a stop, or a region whose keys the menu takes fails. | `audit:DropdownMenu/accessibility` | | ||
| | FR17, AR6 | `DropdownMenu.test.tsx` "skips aria-disabled menuitemradio and menuitemcheckbox items during arrow navigation" and "typeahead skips an aria-disabled item and matches the next enabled label" (the shipped behavior OQ1 leaves open); disabled-row cases | Disabled rows | A disabled row that runs a caller handler fails. | `audit:DropdownMenu/accessibility` | | ||
| | FR20, AR7 | `DropdownMenuSubMenu.test.tsx` header and footer cases, including "on a phone"; `SubMenuHeaderFooter.a11y.chromium.spec.ts` | Fine pointer and phone; Escape paths; nested flyouts; pointer leaving | A region inside a menu, an unnamed dialog, a Tab walk that does not wrap, an Escape that closes the root, or a flyout that closes while focus is inside fails. Evidence gap: no test here asserts the rows-alone case under ContextMenu. | `audit:DropdownMenu/accessibility` | |
There was a problem hiding this comment.
ContextMenu.test.tsx:780 ("shows a sub-menu with a header or footer as its rows alone, and says why in development") asserts this case: the row keeps aria-haspopup="menu", no dialog or header field renders, and the dev warning fires. Suggest citing it here and dropping the evidence gap.
There was a problem hiding this comment.
These eight DropdownMenu records can't be approved yet: making them current settles requirements no engineering owner has ruled on at this head, including the pending link-row and sub-menu header/footer decisions, the keyboard and safe-triangle rows, and the dropped requirement that row focus stay perceivable. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Two record fixes are needed either way:
- The Item, RadioItem and SubMenu records list only the forced-colors focus gap; the ordinary-theme focus tint, about 1.1:1 against the menu surface, which the draft recorded, should be named too.
- DropdownMenu PR1 leaves out the menu's
MutationObserverand the media-query listenerpresentation="adaptive"holds, and says the compact-touch query has no listener.
[Automated review]
| - **AR4 — Focus visibility.** Keyboard focus paints only the menu overlay | ||
| tint with no outline, and nothing under forced colors. The shared row | ||
| treatment is `component:DropdownMenuItem` OQ2. |
There was a problem hiding this comment.
This drops the measured ordinary-theme gap the draft recorded ("measures about 1.1:1 against the menu surface"). The row paints focus as --color-overlay-hover with outline: none (DropdownMenuRadioItem.tsx:49); with the default tokens that is 1.09:1 on the light menu surface and 1.15:1 on the dark one. Naming it here and under Current-state impact, and in the Item and SubMenu twins, keeps the gap visible whichever form the requirement takes.
| - **PR1 — Bounded resources.** While open, the pointer menu observes its own | ||
| size and listens for window and visual-viewport resizes and image loads to | ||
| decide whether it scrolls. A mouse press-open adds document pointer-up and | ||
| pointer-cancel listeners for the length of the press. The compact-touch query | ||
| is read once as the menu opens, with no listener. |
There was a problem hiding this comment.
useMenuOverflow.ts also watches the menu with a MutationObserver (childList, characterData, subtree), and with presentation="adaptive" useAdaptivePresentation subscribes to the same compact-touch query's change event while the menu is mounted, so "with no listener" holds only for the drill-in's one-time read. Suggest naming the content observer and: "With presentation=\"adaptive\" the root subscribes to the compact-touch query while mounted; the drill-in reads it once as the menu opens."
There was a problem hiding this comment.
These eight DropdownMenu records can't be approved yet: making them current settles requirements no engineering owner has ruled on at this head, including the pending link-row and sub-menu header/footer decisions (now FR10 and FR20), the keyboard and safe-triangle rows (FR12, FR13), and the dropped requirement that row focus stay perceivable. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Separately, PR1 in DropdownMenu.spec.md counts only the mouse press-open's document pointerup/pointercancel listeners, but a held finger that opens the menu adds the same pair, so that sentence needs to cover both. The focus-contrast and observer fixes from the last round check out.
[Automated review]
| and image loads to decide whether it scrolls. A mouse press-open adds document | ||
| pointer-up and pointer-cancel listeners for the length of the press. With |
There was a problem hiding this comment.
A held finger that opens the menu adds the same pair: after the 500 ms long press, useMenuPress calls the trigger's press handler with the touch pointer type, and handleTriggerPress runs holdInvokerThroughPress, which adds capture pointerup and pointercancel listeners on the document until that finger lifts (DropdownMenu.tsx:1383, :508-527). FR8 already counts the held finger as a press-open. Suggest:
and image loads to decide whether it scrolls. A press-open, by a mouse press or
a held finger, adds document pointer-up and pointer-cancel listeners for the
length of the press. With
…ontracts as current
…ir shipped contracts
…ied-click close, and the checkbox Enter gap
… drops the undecided DEC-7
…e ContextMenu rows-alone case
…e menu's observers
0448bdc to
5e39a8e
Compare
There was a problem hiding this comment.
These eight DropdownMenu records can't be approved yet: making them current settles requirements no engineering owner has ruled on at this head, including the pending link-row and sub-menu header/footer decisions (now FR10 and FR20), the keyboard and safe-triangle rows (FR12, FR13), and the dropped requirement that row focus stay perceivable. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Separately, FR10 in DropdownMenu.spec.md and FR3 in DropdownMenuItem.spec.md say a middle click on a link row always closes the menu, but only the pointer menu's row closes, and only when hasCloseOnSelect is on, while the touch sheet stays open, so both clauses need scoping. The PR1 listener fix from the last round checks out.
[Automated review]
| | FR7 | `menuMaxHeight` replaces the 300px term of the menu's and its popover viewport's block-size cap while the viewport gutters still bound both; the touch sheet ignores it. | DEC-2 | | ||
| | FR8 | The Trigger button opens the pointer menu on a mouse press-down, with a settle rule on the opening release, and pressing the trigger of an open menu closes it without reopening in the same gesture; a mouse click and the keyboard open as before. The mouse press-open is shipped behavior, not a requirement until `module:DropdownMenu/useMenuPress` DEC-4 is ruled. Under a finger the trigger MUST open the menu as `spec:AST-094` FR29 states: as the finger presses in a fixed bar, after the 150 ms content-touch delay or at a tap's lift inside a scroll view; `presentation="popover"` keeps the tap open. Implementation gap: a held finger opens it after a 500 ms hold, and a tap opens it at its click. | `module:DropdownMenu/useMenuPress` FR8, FR9; `spec:AST-094` FR29 | | ||
| | FR9 | With `renderTrigger`, the rendered control carries `aria-haspopup`, `aria-expanded`, `aria-controls` and the `id` the menu's `aria-labelledby` points at, and receives the same press-model, keyboard-open and toggle-click handlers as the Button path; the menu is named by that control. A disabled custom trigger never opens the menu or the sheet. `button` and `renderTrigger` are mutually exclusive: both given, `renderTrigger` wins and a dev warning fires, in both presentations. A drilled-in Back row (FR11) returns to the menu by that same name: the caller's `aria-label` when given, else the control's accessible name, read as the menu opens. | DEC-3 | | ||
| | FR10 | An action row with `href` renders as one anchor carrying `role="menuitem"` (the pointer menu) or the touch sheet's link row, routed through `LinkProvider`; a plain click runs `onClick`, then closes the menu (`hasCloseOnSelect`), then lets the browser navigate; a modified click does not run `onClick`, closes the menu, and is left to the browser; a middle click closes the menu and runs the caller's `onAuxClick`; a right click is left to the context menu; Enter and Space activate through a synthesized click that carries the key's modifiers. A disabled link row has no `href`; a blocked destination renders inertly. | Current behavior; `family:navigation-destinations` | |
There was a problem hiding this comment.
The middle-click clause covers more than ships. Only the pointer row handles auxclick, and it closes only when hasCloseOnSelect is on, before the caller's onAuxClick (DropdownMenuItem.tsx:235-250, :288). The touch sheet's link row reacts to the click capture alone (MenuBottomSheetActionList.tsx:140-141), so a middle click there opens the tab and leaves the sheet open. Suggest: "in the pointer menu a middle click closes the menu (hasCloseOnSelect), then runs the caller's onAuxClick; the touch sheet leaves a middle click to the browser and stays open".
| | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | ||
| | FR1 | The pointer row exposes one `role="menuitem"` root. Label, description, icon, and end content are presentational content of that row rather than nested controls. | `spec:AST-020` FR15; current source and docs | | ||
| | FR2 | Enabled activation runs the row callback before optional close. Disabled rows expose `aria-disabled=true`, remove link destinations, and run neither callback nor close. | Current behavior and tests | | ||
| | FR3 | A destination row renders as the link root through LinkProvider. Plain activation runs the callback then closes and navigates; a modified click does not run the callback, closes the menu, and is left to the browser; a middle click runs the caller's `onAuxClick` and closes the menu; blocked schemes are inert. | `family:navigation-destinations` FR1–FR5; current tests | |
There was a problem hiding this comment.
Same here: the close follows the row's hasCloseOnSelect and comes before the caller's onAuxClick (DropdownMenuItem.tsx:235-250, :288). Suggest: "a middle click closes the menu (hasCloseOnSelect), then runs the caller's onAuxClick".
There was a problem hiding this comment.
These eight DropdownMenu records can't be approved yet: making them current settles requirements no engineering owner has ruled on at this head, including the pending link-row and sub-menu header/footer decisions (now FR10 and FR20), the safe-triangle row (FR13), and the dropped requirement that row focus stay perceivable. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. The middle-click wording in FR10 and DropdownMenuItem FR3 now matches the source, and nothing else needs changing.
[Automated review]
|
/approve-spec 90d9a7b |
Exact decision needed
Make the DropdownMenu records current, recording shipped behavior: promote
component:DropdownMenu,component:DropdownMenuItem,component:DropdownMenuDivider,component:DropdownMenuRadioGroup,component:DropdownMenuRadioItem, andcomponent:DropdownMenuSubMenufromdraft, and addcomponent:DropdownMenuCheckboxItemandcomponent:DropdownMenuGroup(each has its own.doc.mjs). No behavior, API, default, or DOM change.Existing authority searched
pnpm specs:for packages/core/src/DropdownMenu/DropdownMenu.tsx→ current records that govern DropdownMenu:module:DropdownMenu/useMenuPress,spec:AST-020FR15,spec:AST-086,spec:AST-094,spec:AST-102,design:reduced-motion,family:overlay-dismissal,family:navigation-destinations,architecture:component-theming-surface,architecture:interaction-modality,architecture:layer-runtime,architecture:public-component-api,architecture:react-component-runtime, and the delegated Button, BottomSheet, List, Indicator, Icon, Text, and Divider records.component:DropdownMenuOQ1 with DEC-14, which stays open here); fix(DropdownMenu, ContextMenu): Tab from any sub-menu level closes the whole menu #8870 rebases on top of this record (Tab from a sub-menu level); DropdownMenu: under a finger, the opening finger picks only after UIMenu's handoff (AST-094 FR29) #8627 and refactor(DropdownMenu): a failed sheet preload is the preload's result, not an empty catch #9069 touch DropdownMenu source and rebase on top. docs(spec): useMenuPress states its boundary with core's group tracker #8572 editsuseMenuPress.spec.md, a module outside this PR.module:DropdownMenu/useMenuHoverstaysdraftand out of scope; it is listed inmodulesonly becausecheck:knowledgerequires the owning component to list it.Decision
approved_by: cixzhang,approved_at: 2026-10-31), template 7, written from what main ships today; existing content and attribution are kept.module:DropdownMenu/useMenuPressDEC-4 still lists as proposed, is written as shipped behavior, not a requirement until that decision is ruled.menuMaxHeighttext and decider, which sat under a DEC-7 heading, are back under DEC-2, and the DEC-7 heading (safe triangle), which never had a body or decider of its own, is removed, so FR13 is written as current behavior with no owner decision taken.component:DropdownMenuFR14 and FR16 now point at the new Group and CheckboxItem records.Rejected alternatives
None.
Claim scope and non-goals
Shipped behavior only, traced from the DropdownMenu sources, docs, and tests. No code is fixed. Open questions stay open:
component:DropdownMenuOQ1 (disabled rows and the keyboard),component:DropdownMenuItemOQ1–OQ2 (disabled rows; forced-colors focus),component:DropdownMenuRadioItemOQ1,component:DropdownMenuSubMenuOQ1.Evidence and implementation relationship
DropdownMenu.test.tsx,DropdownMenuSubMenu.test.tsx,DropdownMenuSelectable.test.tsx,BusyOwnerAnnouncement.test.tsx,Menu.a11y.test.tsx,ContextMenu.test.tsx,Breadcrumbs.test.tsx, and the DropdownMenu Chromium suites; each verification row names the cases it relies on and lists its evidence gaps.Current-state impact:
spec:AST-094FR29, FR31, FR32.component:DropdownMenuItemOQ2); Enter on a checkbox row keeps the menu open with no named exception (component:DropdownMenuCheckboxItemOQ1).component:Button's reduced-motion and fixed-height gaps; a drilled-in sub-menu reset by the root closing reports noonOpenChange(false).Writing rubric results
Scope
Validation
pnpm check:knowledge: alignedgit diff --check: clean