Repository navigation
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 needs an engineering owner's decision before approval, and it also needs changes. Two public additions have no governing spec yet: update(options) on the handle createMenuPress returns, and deprecating DropdownMenuTriggerProps.onPointerDown/onContextMenu in favour of a press delivered through ref. An engineering owner should identify or provide the spec for them and record the decision by commenting /approve-spec <full head SHA>; the author can do this as an engineering owner. Independently:
spec:AST-017FR29c asks maintained packages to migrate in the deprecation patch, but DropdownMenu and the five item-only menus (BreadcrumbItem, NavHeadingMenu, TopNavMenu, SideNavHeading, TopNavHeading) stay onuseMenuPress({menuRef, …}), and the ref form has no replacement for theirpressOnItemsOnlyfilter.- ContextMenu's
listRefalso sits on theitemsbottom-sheet container, so after a drill-in the whole sheet becomes a press root and the Back button loses its pressed paint under a coarse pointer. - At 3,145 authored lines, landing the long-press detector extraction separately would keep this PR to the INV22 move, and the StrictMode factory test should observe every
destroyit claims.
[Automated review]
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [feat] `createMenuPress(menu, options)` runs the menu press model on any element without React: the row under a release acts, the highlight follows a held pointer, a mouse press on `trigger` opens the menu, and a finger held on it opens it after the long-press delay. `destroy()` removes every listener it added (`spec:AST-107` FR6). `useMenuPress(menuRef, options)` is its React form: `triggerRef` names the trigger, and there is nothing to spread. The options form, `useMenuPress({menuRef, …})` with its `menuProps` and `triggerProps`, still works and is deprecated: migrate to `useMenuPress(menuRef, {triggerRef, …})` and drop the spreads. It is removed at the next minor. Core's own users of the options form, which move at that minor: DropdownMenu, for its trigger's props, and the menus that track only presses on their items (BreadcrumbItem, NavHeadingMenu, TopNavMenu, and the SideNav and TopNav heading menus). |
There was a problem hiding this comment.
spec:AST-017 FR29c: "Maintained Astryx packages and authoring surfaces migrate in the deprecation patch." This Changeset defers DropdownMenu and the five item-only menus to the next minor, and the ref form has no replacement for their pressOnItemsOnly filter, so a consumer menu that filters the spread handler has no working migration either.
| // menu open. | ||
| const menuPress = useMenuPress({ | ||
| menuRef: listRef, | ||
| useMenuPress(listRef, { |
There was a problem hiding this comment.
listRef is also the ref on the items bottom-sheet container (line 815), which never spread menuProps before. After a drill-in re-render the factory marks that container with data-astryx-menu-press, so pressFeedback.ts:375 stops painting a mouse or Space press on the sheet's Back button under a coarse pointer. A ref that only renderedMenu carries would keep the base behavior.
| // Every factory the hook created is destroyed: StrictMode's replayed | ||
| // effect destroyed the first before creating the second. | ||
| expect(destroys.every(d => d.mock.calls.length <= 1)).toBe(true); | ||
| expect(handles.length - 1 + 1).toBe(handles.length); |
There was a problem hiding this comment.
This assertion is always true, and the destroy spies are attached after render, so the factory StrictMode's replay destroys is never observed. Spying each handle as it is created (wrapping the mocked createMenuPress) and asserting that created equals destroyed after unmount would prove the test's name.
| /** End the gesture in flight with nothing acting (the menu closed). */ | ||
| cancel: () => void; | ||
| /** Take new options: callbacks, the trigger, the row selector and the rest. */ | ||
| update: (options: CreateMenuPressOptions) => void; |
There was a problem hiding this comment.
architecture:public-component-api INV22 and spec:AST-107 FR6 name only destroy() on a factory's handle, and no current record decides an update(options) member. An engineering owner should confirm this public shape, or the hook could re-create the factory when its options change.
f0907e7 to
492c328
Compare
There was a problem hiding this comment.
Approval waits on an engineering owner's decision, and the pull request also needs changes. No current spec decides two public additions: update(options) on the handle createMenuPress returns, and deprecating DropdownMenuTriggerProps.onPointerDown/onContextMenu so the press reaches a renderTrigger control through ref; an engineering owner (the author is one) should identify or provide the governing spec and record the decision by commenting /approve-spec <full head SHA>. Independently:
spec:AST-017FR29c asks maintained packages to migrate in the deprecation patch, but DropdownMenu and the five item-only menus stay onuseMenuPress({menuRef, …}), and the ref form has no replacement for theirpressOnItemsOnlyfilter.- ContextMenu's
listRefalso sits on theitemsbottom-sheet container, so after a drill-in the whole sheet becomes a press root and Back loses its pressed paint under a coarse pointer. - At 3,050 authored lines, landing the long-press detector extraction separately would keep this to the INV22 move, and the StrictMode factory test should observe every
destroyit claims.
[Automated review]
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [feat] `createMenuPress(menu, options)` runs the menu press model on any element without React: the row under a release acts, the highlight follows a held pointer, a mouse press on `trigger` opens the menu, and a finger held on it opens it after the long-press delay. `destroy()` removes every listener it added (`spec:AST-107` FR6). `useMenuPress(menuRef, options)` is its React form: `triggerRef` names the trigger, and there is nothing to spread. The options form, `useMenuPress({menuRef, …})` with its `menuProps` and `triggerProps`, still works and is deprecated: migrate to `useMenuPress(menuRef, {triggerRef, …})` and drop the spreads. It is removed at the next minor. Core's own users of the options form, which move at that minor: DropdownMenu, for its trigger's props, and the menus that track only presses on their items (BreadcrumbItem, NavHeadingMenu, TopNavMenu, and the SideNav and TopNav heading menus). |
There was a problem hiding this comment.
spec:AST-017 FR29c: "Maintained Astryx packages and authoring surfaces migrate in the deprecation patch." This defers DropdownMenu and the five item-only menus to the next minor, and the ref form has no replacement for their pressOnItemsOnly filter, so a consumer menu that filters the same way has no working migration either.
| // menu open. | ||
| const menuPress = useMenuPress({ | ||
| menuRef: listRef, | ||
| useMenuPress(listRef, { |
There was a problem hiding this comment.
listRef is also the ref on the items bottom-sheet container (line 815), which never carried the menu marker before. After a drill-in the factory marks that container with data-astryx-menu-press, so pressFeedback.ts:335 stops painting a mouse or Space press on the sheet's Back button under a coarse pointer. A ref that only renderedMenu carries would keep the base behavior.
| // Every factory the hook created is destroyed: StrictMode's replayed | ||
| // effect destroyed the first before creating the second. | ||
| expect(destroys.every(d => d.mock.calls.length <= 1)).toBe(true); | ||
| expect(handles.length - 1 + 1).toBe(handles.length); |
There was a problem hiding this comment.
This assertion is always true, and the destroy spies are attached after render, so the factory StrictMode's replay destroys is never observed. Spying each handle as it is created (wrapping the mocked createMenuPress) and asserting created equals destroyed after unmount would prove the test's name.
| const options: UseMenuPressRefOptions = isRefForm | ||
| ? second | ||
| : (first as UseMenuPressOptions); | ||
| // The latest options, read by the press at event time, as they always were. |
There was a problem hiding this comment.
Non-blocking: "as they always were" narrates history. Suggest: // The press reads the latest options at event time.
492c328 to
4935463
Compare
|
At 4935463, rebuilt on #9642 (the long-press detector, split out as suggested):
|
There was a problem hiding this comment.
Approval waits on an engineering owner's decision, and two lines need fixing either way.
No current spec settles two record changes this pull request proposes: module:DropdownMenu/useMenuPress DEC-9 with OQ2, which keeps useMenuPress({menuRef, …}) undeprecated until its callers can migrate, although architecture:public-component-api INV22 says the old signature is deprecated; and component:DropdownMenu DEC-11, which keeps DropdownMenuTriggerProps.onPointerDown and onContextMenu until a ref path exists. An engineering owner records the decision by commenting /approve-spec <full head SHA>; the author may do so as an engineering owner.
Independently:
useMenuPress.doc.mjsstill tells builders thatcreateMenuPressreturnsupdate(lines 104 and 175), which the handle no longer has, and it drops the options form'smenuPropsandtriggerPropsreturn rows while that form stays supported.- DEC-11 names
cixzhangas its decider on 2026-10-31, but no record or comment shows that ruling, so it should read as proposed and pending owner review, as DEC-9 does.
Otherwise the factory, the ref form and the five migrated menus behave as on main.
[Automated review]
| type: '() => void', | ||
| description: | ||
| 'End the gesture in flight with nothing acting, for when the menu closes under it.', | ||
| 'End the gesture in flight with nothing acting, for when the menu closes under it. Nothing is spread: the press listens on the menu and its trigger itself and marks the menu root with data-astryx-menu-press. Without React, createMenuPress(menu, options) takes the same options with the trigger as an element (trigger) and returns {isTriggerClickFromPress, cancel, update, destroy}; destroy removes every listener it added.', |
There was a problem hiding this comment.
The handle has no update any more (createMenuPress.ts:145-159). This should read {isTriggerClickFromPress, cancel, destroy}; a builder following it gets a type error, or a TypeError in plain JavaScript.
| 'true = the trigger click belongs to the press that already opened / closed; ignore it.', | ||
| cancel: 'end the gesture w/o acting.', | ||
| cancel: | ||
| 'end the gesture w/o acting. Nothing to spread. No React: createMenuPress(menu, {trigger, …}) → {isTriggerClickFromPress, cancel, update, destroy}.', |
There was a problem hiding this comment.
Same here: createMenuPress(menu, {trigger, …}) → {isTriggerClickFromPress, cancel, destroy}.
|
|
||
| **Reference:** `component:DropdownMenu/DEC-11` | ||
|
|
||
| **Decider:** `cixzhang`, 2026-10-31 |
There was a problem hiding this comment.
No record or comment shows a 2026-10-31 ruling on this, and the description says DEC-11 waits on /approve-spec. Suggest ### DEC-11 (proposed) and **Decider:** proposed by vjeux, 2026-11-02; pending owner review, as DEC-9 is written, until an owner rules.
| // The ref form: createMenuPress on the element behind the ref. The factory | ||
| // keeps the options it is created with, so its callbacks call the latest | ||
| // ones, and a new factory replaces it when its menu, the menu's document, the | ||
| // trigger, a value option, or the set of given callbacks changes. |
There was a problem hiding this comment.
Non-blocking (INV29): one 40-word sentence on several topics. Suggest:
// The factory keeps its creation options. Its callbacks call the latest ones.
// A new menu, document, trigger, value option or callback set creates a new factory.| // the only touch way to open a surface at the press point. | ||
| // | ||
| // A caller can spread the touch handlers, or call `start`, `moveTo` and | ||
| // `cancel` from its own pointer events, as `useMenuPress` does. Both ways use |
There was a problem hiding this comment.
Non-blocking: useMenuPress no longer drives these handlers; the press uses longPressDetector directly, and useLongPressDetector now has no caller but useLongPress. Suggest updating this comment and the @position line above, and folding useLongPressDetector into useLongPress.
4935463 to
499c662
Compare
|
At 499c662:
The owner decision on DEC-9 with OQ2 and on DEC-11 waits on the spec owners. |
There was a problem hiding this comment.
Approval waits on an engineering owner's decision, and one fix is needed either way.
No current spec settles two record changes this pull request proposes: module:DropdownMenu/useMenuPress DEC-9 with OQ2, which keeps useMenuPress({menuRef, …}) undeprecated until its callers can migrate although architecture:public-component-api INV22 says the old signature is deprecated, and component:DropdownMenu DEC-11, which keeps DropdownMenuTriggerProps.onPointerDown and onContextMenu until a ref path exists. An engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so if they are an engineering owner.
Required either way:
- The ref form creates no press while its menu element is unmounted (
useMenuPress.ts:303-305), so a menu rendered only while open never opens on a trigger press-down, yetuseMenuPress.doc.mjsrecommends that form for any new menu, itstriggerRefrow and the changeset state no such condition, and line 27 says the hook hands the press the latest options although the factory keeps its creation options.
Otherwise the factory, the ref form and the five migrated menus behave as on main, and the earlier notes on update in the hook docs and on DEC-11's decider are resolved.
[Automated review]
| delayMs: options.longPressDelayMs ?? MENU_PRESS_LONG_PRESS_MS, | ||
| attached?.press.destroy(); | ||
| attachedRef.current = | ||
| menu == null |
There was a problem hiding this comment.
While menuRef.current is null this creates no press, so the element behind triggerRef gets no listener either: a menu rendered only while open never opens on a trigger press-down or a held finger, and isTriggerClickFromPress() stays false, so only the click toggles it. With the menu rendered only while open, a mouse pointerdown on the trigger calls onTriggerPress in the options form and never in this form. Please state this in the hook docs and the changeset (or attach the trigger without a menu).
| { | ||
| guidance: true, | ||
| description: | ||
| 'Prefer the ref form, useMenuPress(menuRef, {triggerRef, …}), for a new menu: it listens on the menu and the trigger itself, so nothing is spread. The options form, useMenuPress({menuRef, …}), returns menuProps and triggerProps to spread instead.', |
There was a problem hiding this comment.
This recommends the ref form for any new menu, but its press, trigger half included, exists only while the menu element is mounted (useMenuPress.ts:303-305). Suggest adding: "Its press, the trigger's included, exists only while the menu element is mounted; for a menu rendered only while open, use the options form." The dense guidance at line 224 needs the same note.
| name: 'menuRef', | ||
| type: 'RefObject<HTMLElement | null>', | ||
| description: | ||
| 'The menu or listbox root — the surface whose rows a press picks from. The hook creates one press for the element behind the ref, hands it the latest options at each commit, creates a new one when the ref moves to another node, and destroys it at unmount.', |
There was a problem hiding this comment.
The factory keeps its creation options; the hook calls the latest callbacks and creates a new press when a value option changes, which ends a press in flight. Suggest: "The hook creates a press for the element behind the ref after each commit, calls the latest callbacks, creates a new press when the menu, its document, the trigger, a value option or the set of callbacks changes, and destroys it at unmount."
| getOptions: () => options, | ||
| }); | ||
| const disconnect = core.connect(); | ||
| // The highlight and the rows' hover focus follow the last input of the |
There was a problem hiding this comment.
Non-blocking (comment style): one 40-word sentence. Suggest:
// The highlight follows the last input in the menu's document. Track the
// page's document, and the menu's own document when it differs, until destroy.| // `cancel` from its own pointer events, as `useMenuPress` does. Both ways use | ||
| // this hook's hold time, movement tolerance and timer, so a long press means | ||
| // the same thing on a context menu and on a menu trigger. | ||
| // `cancel` from its own pointer events. The menu press runs longPressDetector |
There was a problem hiding this comment.
Non-blocking (comment style): one 32-word sentence. Suggest:
// `cancel` from its own pointer events. The menu press runs longPressDetector
// directly. The hold time, tolerance and timer are the same, so a long press
// means the same on a context menu and on a menu trigger.499c662 to
fc210bc
Compare
|
At fc210bc:
|
There was a problem hiding this comment.
Approval waits on an engineering owner's decision on two proposed spec records; nothing else in the code needs to change.
module:DropdownMenu/useMenuPressDEC-9 (with OQ2) keepsuseMenuPress({menuRef, …})undeprecated beside the newuseMenuPress(menuRef, options)until the ref form can start a press only on an item.architecture:public-component-apiINV22 says the old signature is deprecated under INV9, and INV21 allows a second API for one job only while the first is being deprecated. An owner either approves that exception to INV22 or asks for the deprecation now, with every maintained caller migrated in this patch (spec:AST-017FR29c).component:DropdownMenuDEC-11 is a new decision no current record states: the trigger'sonPointerDown/onContextMenuare deprecated only together with a ref path and every caller's migration.
An engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. Earlier feedback is addressed: the ref form's docs and changeset now say its press, trigger included, needs a mounted menu. The factory, the ref form and the event-clock change otherwise match INV22, spec:AST-107 FR6 and spec:AST-094 FR16, and a busy-page long press now opens the menu in Chromium where the base drops it.
[Automated review]
There was a problem hiding this comment.
Approval waits on an engineering owner's decision on proposed spec changes, and three fixes are needed either way.
- The changeset says DropdownMenu, BreadcrumbItem, NavHeadingMenu, TopNavMenu and the SideNav and TopNav heading menus move to the ref form "with no change in behavior", but a NavHeadingMenu caller's
onPointerDownnow runs after the press starts and itspreventDefault()no longer cancels the press (NavHeadingMenu.tsx:225); name this and DropdownMenu's handler-order changes in the changeset, or run the caller's handler first again. useMenuPress.doc.mjs(lines 27, 123 and 174), thecreateMenuPressJSDoc (createMenuPress.ts:827-829) and theuseMenuPress.tsheader (lines 11-15) still describe the previous revision: a new press on every option change, noupdate(), and six menus on the options form.- The
@deprecatedtags onDropdownMenuTriggerProps.onPointerDownandonContextMenu(DropdownMenu.tsx:375,:380) and the changeset name no replacement, which AST-017 FR29b requires; say to forwardrefto the control.
No current spec decides three parts of this change: update(), startBehavior and the forwarded trigger handlers (proposed DEC-9), deprecating the two trigger press handlers (proposed DEC-11), and NavHeadingMenu's new handler order. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. The factory, the ref form, the deprecation of the options form and the caller migration otherwise match architecture:public-component-api INV22 and AST-017 FR29c, and the touched suites pass.
[Automated review]
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [feat] `createMenuPress(menu, options)` runs the menu press model on any element without React: the row under a release acts, the highlight follows a held pointer, a mouse press on `trigger` opens the menu, and a finger held on it opens it after the long-press delay. `update(options)` merges new options, which apply from the next event without ending a press in progress. `startBehavior: 'items'` starts a press only on an item, so other content in the menu keeps its own clicks. `destroy()` removes every listener it added. `useMenuPress(menuRef, options)` is its React form: `triggerRef` names the trigger, and there is nothing to spread. Its press exists only while the menu element is mounted, so a menu that opens on a press of its trigger keeps its element mounted while closed. The options form, `useMenuPress({menuRef, …})` with its `menuProps` and `triggerProps`, is deprecated; it still works as before. `DropdownMenuTriggerProps.onPointerDown` and `onContextMenu` are deprecated too: the menu listens on the trigger itself. DropdownMenu, BreadcrumbItem, NavHeadingMenu, TopNavMenu and the SideNav and TopNav heading menus move to the ref form with no change in behavior. |
There was a problem hiding this comment.
"No change in behavior" does not hold for NavHeadingMenu: a caller's onPointerDown now runs after the factory's native listener, sees defaultPrevented already true for a finger, and can no longer cancel the press (it could through composeEventHandlers before). DropdownMenu's menu now also receives a caller onPointerDown the press handler used to replace, and button.onPointerDown runs after the press opens the menu. Please name these here.
| ENABLED_ITEM_SELECTOR, | ||
| ), | ||
| )} | ||
| onPointerDown={onPointerDownProp} |
There was a problem hiding this comment.
This handler now runs after the press has started. A finger that slides from one row to another picks the second row even when this onPointerDown calls preventDefault(), where nothing acted before. Either restore the order (the caller first, and a cancelled event skips the press) or record the new order with an owner's approval.
| name: 'menuRef', | ||
| type: 'RefObject<HTMLElement | null>', | ||
| description: | ||
| 'The menu or listbox root — the surface whose rows a press picks from. The hook creates a press for the element behind the ref after each commit, calls the latest callbacks, creates a new press when the menu, its document, the trigger, a value option or the set of callbacks changes, and destroys it at unmount. While the element is unmounted there is no press, not even on the trigger.', |
There was a problem hiding this comment.
At this head a changed trigger, value option or callback set goes through update() on the same press (useMenuPress.ts:238-251); only a new menu or document creates a new press. The cancel row (line 123) also lists the handle as {isTriggerClickFromPress, cancel, destroy}, and the dense rows (174, 201) disagree with it. The returns table omits the ref form's handleTriggerPointerDown and handleTriggerContextMenu.
| * Destroy it to remove every listener it added. It keeps the options | ||
| * it is created with: to change them or the trigger, destroy it and create | ||
| * another. |
There was a problem hiding this comment.
This contradicts update() (line 167). Suggest: "Destroy it to remove every listener it added. update changes its options or trigger from the next event."
| * @position Core hook; a thin wrapper over createMenuPress, in its ref form | ||
| * for ContextMenu, DropdownMenuSubMenu, MenuBottomSheetActionList, Selector | ||
| * and TabMenu. The options form, which returns props to spread, stays with | ||
| * DropdownMenu (its trigger's props), BreadcrumbItem, NavHeadingMenu, | ||
| * TopNavMenu, and the SideNav and TopNav heading menus. |
There was a problem hiding this comment.
Every Core menu now uses the ref form, so this header is out of date. Suggest: "@position Core hook; a thin wrapper over createMenuPress. Every Core menu uses the ref form; the options form is deprecated."
| * @deprecated The menu listens on the trigger itself, so spreading this | ||
| * adds nothing. |
There was a problem hiding this comment.
AST-017 FR29b asks every deprecation warning to name the replacement. Suggest: @deprecated Forward ref to the control; the menu listens on it. (same for onContextMenu at line 380 and in the changeset). A control that takes these handlers but not ref keeps the press only through them, so "adds nothing" is not true for it.
There was a problem hiding this comment.
Approval waits on an engineering owner's decision about one behavior this pull request carries; no other change is needed.
This review covers the whole diff against main, including the useAttachment commit from #9910. The record changes (module:DropdownMenu/useMenuPress DEC-9, DEP-0019 and CLN-0030, and component:DropdownMenu DEC-11 with the amended FR9 and spec:AST-055 FR2) are owner-approved for this head, and the ref form now attaches through useAttachment, which resolves the earlier shared-lifecycle finding.
What must be settled:
useAttachmentnow releases and re-attaches every attached element (gesture recognizers, press tracker, finger scroll, pager, animator and the menu press) after the element moves to another document, at its component's next commit and for every pointer (packages/core/src/hooks/useAttachment.ts:79-95). No current record decides this:architecture:react-component-runtimeINV5 and INV6 cover a replaced or unmounted node, not one that moves to another document. An engineering owner, the author included, can approve it in a comment on this pull request, or add it to a record and approve that with/approve-spec <full head SHA>.
Two non-blocking suggestions are inline: a comment in useMenuPress.ts that repeats another, and changeset wording that says the re-attach happens at the next render.
[Automated review]
| nodes.every( | ||
| (node, index) => | ||
| node === current.nodes[index] && | ||
| node.ownerDocument === current.documents[index], |
There was a problem hiding this comment.
This makes every useAttachment caller (gesture recognizers, press tracker, finger scroll, pager, animator, menu press) release and re-attach after its node moves to another document, for every pointer. No current record settles that yet: architecture:react-component-runtime INV5 and INV6 cover a replaced or unmounted node. Approval waits on an engineering owner's decision on this behavior.
| // Attaches a factory to the menu behind the ref. The options form attaches | ||
| // none: its ref is one that never holds a menu. |
There was a problem hiding this comment.
Non-blocking (INV29): this restates the NO_MENU comment at line 240 and narrates the useAttachment call below. Suggest deleting it; line 240 already gives the reason.
| '@astryxdesign/core': patch | ||
| --- | ||
|
|
||
| [fix] An element's gesture recognizers, press tracking, pager and animator attach again when the element moves to another document, such as a Picture-in-Picture window, instead of staying bound to the old one. |
There was a problem hiding this comment.
Non-blocking: the re-attach happens at the component's next commit, not at the move itself. Suggest: "attach again in the document an element moved to, such as a Picture-in-Picture window, at its component's next render".
5aba2a8 to
578e4a5
Compare
|
/approve-spec 578e4a5 |
There was a problem hiding this comment.
Changes requested. The record changes are approved for this head and Core's menus keep their mouse behavior, but the astryx upgrade codemod still rewrites two shapes that change a trigger's press without leaving a TODO, which spec:AST-017 FR29c rules out.
- Miss: a trigger that a component element or a render callback mounts is rewritten without the placement check the menu already gets (
migrate-use-menu-press-to-ref-form.mjs:426-446), and because the ref form readstriggerRefonly at the caller's commits, a trigger mounted in a later commit loses its press after the rewrite. - Miss: a
renderTriggerforward such as<MyButton {...p} {...tooltipProps} onPointerDown={p.onPointerDown} />is dropped (migrate-use-menu-press-to-ref-form.mjs:724-733) although it is what lets the menu's handler win over the other spread, so after the rewrite a mouse press-down no longer opens the menu.
Leaving the first shape with a TODO and the second unchanged, and adding both to the codemod's tests, resolves this. Separately, the attachment-follows-document changeset says no caller opts in, but useMenuPress does in this pull request.
[Automated review]
| ref == null || | ||
| // A component may drop the two handlers, or give its ref to an | ||
| // imperative handle that takes no listeners. | ||
| !isDomElement(spread.parent.parent.node) || |
There was a problem hiding this comment.
Miss: this loop checks only that the trigger element is a DOM element, not where it sits. The ref form reads triggerRef.current only at the caller's commits (useMenuPress.ts:168, :280), so a trigger inside a component element or a render callback that mounts it in a later commit loses its press after the rewrite, with no TODO (spec:AST-017 FR29c). Apply the menu's placement check (rendersWithComponent, or isAlwaysRendered) to each trigger spread, and add those two shapes to the TODO test.
| // still passes the handler, under any ancestor. | ||
| if ( | ||
| attribute == null || | ||
| !takesTriggerRef(j, attribute.parent.node, [param.name], []) |
There was a problem hiding this comment.
Miss: takesTriggerRef accepts any element that spreads the props, so <MyButton {...p} {...tooltipProps} onPointerDown={p.onPointerDown} /> loses its forward, though the forward is what overrides tooltipProps.onPointerDown. After the rewrite a mouse press-down no longer opens the menu. Drop a forward only when no other spread sits between the props spread and the forward; otherwise leave it, as the other kept forwards are left.
578e4a5 to
724cd1c
Compare
|
/approve-spec 724cd1c |
There was a problem hiding this comment.
Changes requested: one codemod case still breaks the migration guarantee. The record changes (module:DropdownMenu/useMenuPress DEC-9, DEP-0019 and CLN-0030, and component:DropdownMenu DEC-11 with the amended FR9 and spec:AST-055 FR2) are owner-approved for this head, and Core's menus keep their mouse behavior.
Blocking finding:
- Miss:
astryx upgraderewrites a trigger that reached the press only through itstriggerPropsspread to pass its ref astriggerRef, with no TODO, so a mouse press held 300 ms or more and released on that trigger no longer closes the menu it opened (migrate-use-menu-press-to-ref-form.mjs:444-449), which breaks the migration guarantee inspec:AST-017FR29c; leave that call unchanged with a TODO and add it to the TODO test and DEP-0019's codemod row.
The two codemod cases raised last time are fixed and covered by new tests.
[Automated review]
| if (!isLocalRefObject(ref, spread)) { | ||
| return null; | ||
| } | ||
| plan.triggerRef = cloneChain(j, ref); |
There was a problem hiding this comment.
Miss: in the options form this trigger was never the press's trigger for a release, so a release on it counted as outside the menu and dismissed it. Passing it as triggerRef makes the release land on the trigger, so a mouse press held 300 ms or more (or slid into the menu and back) and released here no longer closes the menu. That changes the old result with no TODO (spec:AST-017 FR29c). Leave the call unchanged with a TODO when the trigger is reached only through its spread, and add the case to the TODO test.
|
/approve-spec 546162f |
There was a problem hiding this comment.
Approved pending CI: CI has not run on this head yet. Its required checks wait for a maintainer to add the ci:run label, since the branch is in a fork.
The record changes (DEC-9, DEP-0019, CLN-0030, DEC-11 and the FR9 and AST-055 FR2 amendments) are owner-approved for this head, the codemod now leaves a TODO for a trigger the old call reached only through its spread, and the touched suites pass. Next: a maintainer adds the ci:run label so CI runs; this pull request builds on #9910, which lands first.
- Optional follow-up: INV22's known-gaps line in
docs/architecture/public-component-api.mdstill listsuseMenuPress.
[Automated review]
|
/approve-spec 1120baa |
There was a problem hiding this comment.
Changes requested: one regression needs a fix before approval. The record changes (DEC-9, DEP-0019, CLN-0030, DEC-11 and the FR9 and AST-055 FR2 amendments) are approved for this head, and the touched suites pass.
- Miss: a finger's press never starts in a Core menu rendered inside an open shadow root, because the press deferred to the window reads
event.targetthere, where the browser has retargeted it to the shadow host (packages/core/src/hooks/createMenuPress.ts:724, after the deferral at:998); a finger slide from Apple to Banana in the shadow-root Selector story picks Banana onmainand nothing here, so take the target fromcomposedPath()[0]before deferring and add a shadow-root finger case to the factory tests and the shadow-root browser spec.
CI's workflows have not run on this head yet. This pull request builds on #9910, which lands first.
[Automated review]
| } | ||
| // The innermost model owns the press: a flyout is a DOM descendant of | ||
| // its parent menu, and both would otherwise track the same pointer. | ||
| const target = event.target as Element | null; |
There was a problem hiding this comment.
For a finger, this runs from the window listener that afterPageHandlers adds (line 998), and by then event.target is retargeted to the shadow host when the menu sits in an open shadow root. closest(MENU_PRESS_ROOT_SELECTOR) then misses the menu, so no finger press starts, and the event is already taken, so the React forward cannot start it either. In Chromium a finger slide from Apple to Banana in the shadow-root Selector story picks Banana on main and nothing on this head. Reading the target from composedPath()[0] while the element's own listener runs, before deferring, keeps the released behavior; the startBehavior: 'items' check at line 735 needs the same target.
|
/approve-spec fe52a5b |
There was a problem hiding this comment.
Approved pending CI: CI has not run on this head yet, because the pull request is still a draft; marking it ready for review runs it.
The shadow-root finger regression is fixed: a finger's press in a menu inside a shadow root starts again, and three new factory cases and a Chromium spec case cover it. The record changes (DEC-9, DEP-0019, CLN-0030, DEC-11 and the FR9 and AST-055 FR2 amendments) are approved for this head, and the touched suites pass, desktop parity included.
Non-blocking:
architecture:public-component-apiINV22 still listsuseMenuPressamong its known gaps; a follow-up record edit can drop it.- The description says "OQ2 closed", but
module:DropdownMenu/useMenuPresshas no OQ2.
This pull request builds on #9910, which lands first.
[Automated review]
… for a caller that asks An attachment that listens on its node's document, as the menu press does, must attach again when the node moves into another document, such as a Picture-in-Picture window. useAttachment gains an internal followsDocument option for that. No current caller passes it, so the gesture hooks, the pager and useAnimator keep their behavior: a document move leaves their attachment as it is, and useAnimator keeps its values.
createMenuPress.test.ts runs the factory with no React: the press model on plain elements, every listener gone after destroy (mid-press included), a trigger moved by destroy and create, and a menu in another document. useMenuPress.factory.test.tsx checks the ref form only delegates: one factory per element, the latest callbacks without a new factory, a new factory when a value option changes, the committed callbacks while a transition suspends, and every factory StrictMode creates destroyed once. They fail here: the factory and the ref form come in the next commit.
createMenuPress(menu, options) runs the press model on any element: the row under a release acts, the highlight follows a held pointer, a mouse press on the trigger opens the menu, and a finger held on it opens it after the long-press delay, on the shared long-press detector. It keeps the options it is created with, and destroy() removes every listener it added. useMenuPress(menuRef, options) is its React form, with nothing to spread. It calls the latest callbacks, reads only committed options, and creates a new factory when the menu, its document, the trigger, a value option or the set of given callbacks changes. ContextMenu, DropdownMenuSubMenu, MenuBottomSheetActionList, Selector and TabMenu use it. ContextMenu's press rides its menu element, which carries the press marker in its markup as before, never the items sheet's container, so the sheet's Back keeps its press paint. The options form is unchanged, and its doc keeps its returns. useLongPressDetector folds into useLongPress, since the menu press runs the detector directly. The core API report gains the factory, its types and the ref form. The DropdownMenu record proposes that the trigger props' press handlers leave only with a ref path, and the useMenuPress record that the options form is deprecated together with its callers' migration, both pending owner review.
…u moves to the ref form createMenuPress gains update(options): new options merge with the ones it has, apply from the next event, and never end a press in progress. Its startBehavior option, 'items', starts a press only on an item, so other content in the menu keeps its own clicks and a modified mouse press on a link item stays the browser's. A trigger event that a caller still forwards reaches the press once. useMenuPress's ref form passes each commit's options through update() and creates a new factory only for a new menu or document. DropdownMenu, BreadcrumbItem, NavHeadingMenu, TopNavMenu and the SideNav and TopNav heading menus move to the ref form with no change in behavior, and menuPressOnItems.ts goes. The options form and DropdownMenuTriggerProps.onPointerDown and onContextMenu are deprecated, to go at the next minor. The module record's DEC-9 and the component record's DEC-11 say so, and OQ2 is closed.
DEP-0019 deprecates useMenuPress({menuRef, ...}) and the trigger press
handlers now; CLN-0027 removes them in 0.8.0. An astryx upgrade codemod
moves calls to the ref form and drops plain forwards of the handlers,
leaving a TODO wherever it cannot prove the result. DEC-9 and DEC-11 are
settled.
The press listens for the press-down on the menu's document, after the
page's own handlers, so a caller's handler runs first again and one that
cancels a press-down inside the menu starts no press; DropdownMenu forwards
its Button's events in the released order.
…ld stops the codemod The press listens on the menu and its trigger themselves, and acts on a press-down once it reaches the document, after the page's own handlers. That document listener lives only while the press-down dispatches, so no press-down listener stays on the document while idle (spec:AST-094 FR24; architecture:react-component-runtime INV13), and PR1 keeps its text. component:DropdownMenu FR9 and spec:AST-055 FR2 name the ref path DEC-11 leaves. The codemod walks a menu up only through DOM elements and fragments: a component element may hold its children back while closed, so such a menu gets a TODO.
|
/approve-spec 93f24b8 |
There was a problem hiding this comment.
Approved pending CI: CI has not run on this head yet, because the pull request is still a draft; marking it ready for review runs it.
The record changes (DEC-9, DEP-0019, CLN-0030, DEC-11 and the FR9 and AST-055 FR2 amendments) are approved for this head. The rebase moves DropdownMenu's touch model and the held-finger lit row into createMenuPress's shared core: the touched suites and the core type check pass, a mouse behaves as on main in every Core menu, and a finger slide in DropdownMenu lights the same row as on main.
Non-blocking:
useDropdownMenuPress(packages/core/src/hooks/useMenuPress.ts:298) has no production caller now that DropdownMenu usesuseCoreMenuPress; move its test cases touseCoreMenuPressand delete it, or say why it stays.architecture:public-component-apiINV22 still listsuseMenuPressamong its known gaps; a follow-up record edit can drop it.
This pull request carries #9910's useAttachment change as its first commit.
[Automated review]
| @@ -333,640 +298,107 @@ export function useMenuPress(options: UseMenuPressOptions): UseMenuPressReturn { | |||
| export function useDropdownMenuPress( | |||
There was a problem hiding this comment.
Non-blocking: useDropdownMenuPress has no production caller now that DropdownMenu uses useCoreMenuPress; only useMenuPress.test.tsx calls it. Point those cases at useCoreMenuPress and delete it, or say why it stays.
There was a problem hiding this comment.
Changes requested: the deprecations in this pull request need an explicit owner decision before it can be approved.
The record changes (DEC-9, DEP-0019, CLN-0030, DEC-11 and the FR9 and AST-055 FR2 amendments) are approved for this head, CI passes, and Core's menus keep their mouse behavior; what is missing is an owner decision that names each deprecated symbol, which general spec guidance such as INV22, a Decider: line or the spec approval does not provide.
Blocking findings:
- Newly live check: Callers of
useMenuPress({menuRef, …})start getting deprecation warnings, and the docs and release notes mark it deprecated and removed in 0.8.0 (packages/core/src/hooks/useMenuPress.ts:261); that needs an explicit owner decision for theuseMenuPressoptions form. Found by: deprecation rule. - Newly live check: Code that imports
UseMenuPressOptionsstarts getting deprecation warnings (packages/core/src/hooks/useMenuPress.ts:82); that needs an explicit owner decision forUseMenuPressOptions. Found by: deprecation rule. - Newly live check: Code that imports
UseMenuPressReturnstarts getting deprecation warnings (packages/core/src/hooks/useMenuPress.ts:138); that needs an explicit owner decision forUseMenuPressReturn. Found by: deprecation rule. - Newly live check: Custom DropdownMenu triggers are set to get deprecation warnings for
DropdownMenuTriggerProps.onPointerDownin 0.8.0, which CLN-0030 records (docs/specs/components/DropdownMenu/modules/useMenuPress.spec.md:126); that needs an explicit owner decision forDropdownMenuTriggerProps.onPointerDown. Found by: deprecation rule. - Newly live check: The same CLN-0030 line schedules
DropdownMenuTriggerProps.onContextMenufor deprecation in 0.8.0; that needs an explicit owner decision forDropdownMenuTriggerProps.onContextMenu. Found by: deprecation rule.
To resolve them, cite in the description an explicit owner decision that names each symbol, or take those deprecations out until one exists. Separately, useDropdownMenuPress (packages/core/src/hooks/useMenuPress.ts:298) no longer has a production caller and could move its test cases to useCoreMenuPress and go.
[Automated review]
| /** | ||
| * The options form: props to spread on the menu root and the trigger. The ref | ||
| * form above listens on both itself, so nothing is spread. | ||
| * @deprecated Use the ref form, `useMenuPress(menuRef, options)`. |
There was a problem hiding this comment.
Newly live check: Callers of useMenuPress({menuRef, …}) start getting deprecation warnings, and the docs and release notes mark it deprecated and removed in 0.8.0; that needs an explicit owner decision for the useMenuPress options form, cited in the description. Found by: deprecation rule.
| * the activation to a control inside it (a link) can tell the model's click | ||
| * from the browser's stray one. | ||
| * The options form's options: the menu and the trigger as refs. | ||
| * @deprecated Use the ref form, `useMenuPress(menuRef, options)`. |
There was a problem hiding this comment.
Newly live check: Code that imports UseMenuPressOptions starts getting deprecation warnings; that needs an explicit owner decision for UseMenuPressOptions. Found by: deprecation rule.
|
|
||
| /** | ||
| * What the options form returns: props to spread on the menu and the trigger. | ||
| * @deprecated Use the ref form, `useMenuPress(menuRef, options)`. |
There was a problem hiding this comment.
Newly live check: Code that imports UseMenuPressReturn starts getting deprecation warnings; that needs an explicit owner decision for UseMenuPressReturn. Found by: deprecation rule.
| | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | id | `CLN-0030` | | ||
| | lifecycle | `DEP-0019` | | ||
| | delta | `useMenuPress`'s options form, `UseMenuPressOptions` and `UseMenuPressReturn` are removed; `DropdownMenuTriggerProps`'s `onPointerDown` and `onContextMenu` are deprecated, as the press moves to the trigger's own listener | |
There was a problem hiding this comment.
Newly live check: This row commits 0.8.0 to deprecating DropdownMenuTriggerProps.onPointerDown and onContextMenu, so custom-trigger callers are set to get deprecation warnings then; that needs an explicit owner decision for each of the two handlers. Found by: deprecation rule.
There was a problem hiding this comment.
Approval of this pull request waits on an engineering owner's decision on its record changes at this head, plus one wording fix.
module:DropdownMenu/useMenuPress DEC-9 (the createMenuPress handle's update and event forwards, startBehavior, MenuPressDownEvent, and the rule that a finger's press-down a page handler stops or cancels starts no press in Core's menus) and component:DropdownMenu DEC-11, with the amended FR9 and spec:AST-055 FR2, changed since their last approval; an engineering owner records approval by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner.
Blocking finding:
- DEC-9's first sentence (
docs/specs/components/DropdownMenu/modules/useMenuPress.spec.md:337) says the options form delegates to the factory as INV22 asks, butuseMenuPress(options)drives the factory's internal core and never callscreateMenuPress(packages/core/src/hooks/useMenuPress.ts:315), so please reword it to say the options form stays as released and remains an INV22 known gap.
The earlier deprecation findings no longer apply now that nothing is deprecated, Core's menus keep their mouse behavior (the desktop parity tests pass here and on main), and CI has not run on this head yet because the pull request is a draft; two non-blocking suggestions are inline.
[Automated review]
| **Reference:** `module:DropdownMenu/useMenuPress/DEC-9` | ||
| **Decider:** `cixzhang`, `2026-11-04` | ||
|
|
||
| The options form and Core's menus delegate to the factory, as |
There was a problem hiding this comment.
useMenuPress(options) does not delegate to createMenuPress: it builds the internal createMenuPressCore and returns its handlers as props (packages/core/src/hooks/useMenuPress.ts:315-325), and useMenuPress.factory.test.tsx:548-555 asserts createMenuPress is never called. INV22 asks for delegation to the public factory with the same options, and its known-gap list still names useMenuPress. Suggested: "Core's menus run on the factory. The options form drives the factory's core through the props it returns and stays as released, so it remains an INV22 known gap."
| * and the same Effect's cleanup releases it, so a detached, replaced or moved | ||
| * node releases the old document before the new one is held. | ||
| * With `holdsDocument` false it holds no element document, for a hook whose | ||
| * element another tracker already holds (useMenuPress's ref form, through |
There was a problem hiding this comment.
Non-blocking: this pull request no longer has a ref form of useMenuPress; the caller is Core's internal useCoreMenuPress. Suggested: "for a hook whose element another tracker already holds (Core's menus, through createMenuPress)."
| type: '() => void', | ||
| description: | ||
| 'End the gesture in flight with nothing acting, for when the menu closes under it.', | ||
| "End the gesture in flight with nothing acting, for when the menu closes under it. Without React, createMenuPress(menu, options) takes these options with the trigger as an element (trigger), plus startBehavior ('items' starts a press only on an item), and returns {isTriggerClickFromPress, cancel, update, handleMenuPointerDown, handleTriggerPointerDown, handleTriggerContextMenu, destroy}. Its destroy removes every listener it added.", |
There was a problem hiding this comment.
Non-blocking: this sentence is the only consumer doc for createMenuPress, and it sits inside the cancel return row, so a reader looking up cancel gets the factory's whole API. createAnimator has its own createAnimator.doc.mjs; a createMenuPress doc entry would match it.
The options form, useMenuPress(options), keeps its released contract and is not deprecated. It drives the factory's core through the props it returns, so it remains an INV22 known gap. The public ref-form overload and its two types go; Core's menus keep an internal ref form over the factory. The codemod, its tests and its changeset go, with the DEP-0019 and CLN-0030 rows. createMenuPress(menu, options) is unchanged, so the API report shows only it and its types as new. The internal useDropdownMenuPress had no production caller, so it goes too. Its touch-model cases run on Core's ref form; the other cases stay on the public options form.
There was a problem hiding this comment.
Approval of this pull request waits on an engineering owner's decision on its record changes at this head; no other change is required.
module:DropdownMenu/useMenuPress DEC-9 (the createMenuPress handle's update and event forwards, startBehavior, MenuPressDownEvent, keeping useMenuPress(options) as released, and the rule that a finger's press-down a page handler stops or cancels starts no press in Core's menus) and component:DropdownMenu DEC-11, with the amended FR9 and spec:AST-055 FR2, changed since their last approval; an engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner.
There are no blocking findings: the earlier DEC-9 wording now matches the code, Core's menus keep their mouse behavior (the desktop parity tests pass here and on main), and nothing is deprecated. CI's product jobs have not run on this head because the pull request is a draft, and two non-blocking suggestions are inline.
[Automated review]
| * opening, highlighting, activating and dismissing | ||
| * @output createMenuPress, its option and handle types, the marker attributes, | ||
| * the press constants and the activation mark | ||
| * @position Core factory; useMenuPress is its React wrapper. The transition |
There was a problem hiding this comment.
Miss (non-blocking, INV29): useMenuPress drives this module's core (createMenuPressCore) and never calls createMenuPress, which DEC-9 now says too. Suggested:
* @position Core factory. useMenuPress drives its core; Core's menus wrap it
* through useCoreMenuPress. The transition table lives in menuPressGesture.ts.| type: '() => void', | ||
| description: | ||
| 'End the gesture in flight with nothing acting, for when the menu closes under it.', | ||
| "End the gesture in flight with nothing acting, for when the menu closes under it. Without React, createMenuPress(menu, options) takes these options with the trigger as an element (trigger), plus startBehavior ('items' starts a press only on an item), and returns {isTriggerClickFromPress, cancel, update, handleMenuPointerDown, handleTriggerPointerDown, handleTriggerContextMenu, destroy}. Its destroy removes every listener it added.", |
There was a problem hiding this comment.
Non-blocking: the factory's only consumer doc sits inside the hook's cancel row, so a reader looking up cancel gets the whole createMenuPress API. A createMenuPress.doc.mjs entry, as createAnimator.doc.mjs has, would give it its own page.
|
Closing: the maintainers decided not to add |
Batch: Menus · createMenuPress, the menu press without React.
User need
A menu built without React can't use the menu press model.
Problem and solution fit
INV22 asks every public interaction hook to delegate to a public
create…(element, options)factory.createMenuPress(menu, options)is that factory, and Core's menus run on it.useMenuPress(options)drives the factory's core through its props and stays as released, an INV22 known gap. Nothing is deprecated.Current specification
architecture:public-component-apiINV22 decides the factory and its element-taking signature;spec:AST-107FR6.module:DropdownMenu/useMenuPressDEC-9 (one factory, and Core's menus on it) andcomponent:DropdownMenuDEC-11 (the trigger's press handlers stay);component:DropdownMenuFR9 andspec:AST-055FR2 name the trigger's ref path. No deprecation or cleanup rows.Public delta and caller burden
createMenuPress(menu, {trigger?, itemSelector, startBehavior?, …})returns{isTriggerClickFromPress, cancel, update, handleMenuPointerDown, handleTriggerPointerDown, handleTriggerContextMenu, destroy};update(options)merges options from the next event without ending a press;startBehavior: 'items'starts a press only on an item.useMenuPress(options)keeps its signature, types and behavior. No caller has to change. The API report adds onlycreateMenuPressand its types.Design and alternatives
startBehaviorreplacesmenuPressOnItems.ts.update()replaces; a public ref form of the hook, since the options form stays its one public form.Evidence
startBehavior, the five item-only menus fail six identity cases.update()that merges nothing, four.Writing rubric results
R1, R2, R3: none. R4: DEC-9 names two rejected alternatives. R5: none. R6: none.
Scope
Stack: #9910 → #9504 → #8458.
This PR edits #9910's changeset, so the two merge in one release.
Testing
At f64e8bb, DEC-9 says the options form drives the factory's core and stays an INV22 known gap, and a comment in
interactionModality.tsnames Core's menus instead of a ref form.check:knowledge, Prettier,review:preand the CLI search, resolution, discovery and doc-compiler tests (57 of 57) pass there.At 67ef7d2, the internal
useDropdownMenuPressgoes: it had no production caller. Its three touch-model cases now run on Core's internal ref form, with the forwards DropdownMenu uses, and the other 48 cases stay on the public options form. The core type check, lint, strict lint, Prettier and the menu press units (443 of 443: the hook, the factory, desktop parity and DropdownMenu) pass there.At b078608, the hook's options form is no longer deprecated. The public ref-form overload and its two types go, with the codemod, its tests and changeset, and the DEP-0019 and CLN-0030 rows; DEC-9 and DEC-11 now say what the owner ruled. Core's menus keep an internal ref form over the factory, so their behavior is unchanged. The regenerated API report differs from main's only in the
createMenuPressrows; main's two stalepresentationrows for DropdownMenu and MoreMenu are left to their own fix. Local gates at 73a60e9, which differs only in record and doc text: the core type check,pnpm lint, strict lint, Prettier, the knowledge, changeset and SYNC checks,review:pre, the coreuisuite (18,550 passed; the 4 RadioListItem and Switch source-build tests fail the same way on main in this checkout), touch-support-bundle and resolution-compat (23 of 23), and the CLI node suite (9,805 passed; text-json-parity's two experimental-render cases and resolve-theme fail the same way on main 67ad509; icon-sets and two debug-result-summary cases fail only on the loaded machine and pass alone). On a fresh Storybook build: the Chromium a11y specs of the menus, the navigation menus and Selector (266 of 266) and the menu interaction specs (9 of 9).At 93f24b8, the stack is rebased onto main 5083ead, which carries the DropdownMenu touch model, the lit row and the held paint; here they run in the factory's core. The public factory never reads the touch model; Core's menus pass it through their internal ref form. NOT-FOR-LANDING arms without the touch model (223e154) or without the lit row and held paint (c35edc2) each fail five of its tests. In WebKit, through the container recipe, the drag-highlight and hand-off specs pass (7 passed; 2 pen cases skip, since WebKit has no pen input).
At fe52a5b, a finger's press in a menu inside a shadow root starts again: the press reads the press-down's target at the menu's own listener, before it defers to the window, where the target is the shadow host. Three factory cases fail with 1120baa's factory (NOT-FOR-LANDING arm ff3f843); reading
composedPath()[0]instead (arm db044c1) fails the nested-row case. The shadow-root Chromium spec's finger slide passes here and fails with 1120baa's factory.The desktop parity file passes 110 of 110 here and on main a343c79. NOT-FOR-LANDING arms: 4f1c189, without
followsDocument, and d794cac, without #9910, fail the factory's document-move case; b22776e, the previous press, hook and menus under this pull request's tests, fails 45 (40 parity rows, the finger cancel cases).no-comment-provenance --checkfails the same way on main: its baseline is stale there.