LearnNewsExamplesServices
Frontmatter
id18758
titlemenu.List looks submenus up under a key they are not cached by
stateClosed
labels
bugairegression
assigneesneo-opus-ada
createdAtSep 16, 2026, 10:07 AM
updatedAtSep 16, 2026, 10:58 AM
githubUrlhttps://github.com/neomjs/neo/issues/18758
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 18760 menu.List: Right and Left no longer walk the submenu cascade, [x] 18759 menu.List: a pointer resting on a parent item shows its submenu
closedAtSep 16, 2026, 10:58 AM

menu.List looks submenus up under a key they are not cached by

neo-opus-ada
neo-opus-ada commented on Sep 16, 2026, 10:07 AM

Context

Found while scoping hover submenus (operator request, 2026-09-16): #18759 and #18760 are blocked by this ticket. On dev@d688710175, examples/button/base: with the parent Item 3 already showing its submenu, a MutationObserver armed before a further click on Item 3 recorded zero menu-list additions or removals. #4606 specified that Enter on a parent toggles its submenu, and #4669 routes clicks through the same onKeyDownEnter().

The Problem

src/menu/List.mjs writes its submenu cache under one key and reads it under another:

  • write: showSubMenu() stores under store.getKey(record); toggleSubMenu() reads the same way.
  • read: onKeyDownEnter() and onSelect() look up getItemRecordId(nodeId).

Since #9071 (2026-02-09), list.Base#useInternalId defaults to true, so getItemRecordId() returns the internalId (neo-record-9). A menu record's key is its id (file-open), or -1, -2, … assigned by collection.Base when it has none. The two never match:

  1. The toggle branch in onKeyDownEnter() is unreachable, so Enter and click on an open parent never close it.
  2. The activeSubMenu !== subMenuMap[…] guard in onSelect() is always true. Harmless only because selection.Model#select() calls onSelect() on a change alone.

A second defect sits beside it. showSubMenu() replaces activeSubMenu without unmounting a different submenu that is showing. Today only onSelect() reaches it, and it hides first. Once the toggle path runs again, toggleSubMenu() on a sibling would strand the previous submenu: mounted, with nothing tracking it.

The Architectural Reality

  • list.Base#getItemRecordId() and getRecordId() are the internalId-aware pair. A record's cached submenu is an identity by key, so it resolves through store.getKey(record), like getSubMenuData() already does.
  • Listener order decides what a click does. list.Base registers click → onClick in construct(); the selection model registers click → onListClick in register(), called from onConstructed(); manager.DomEvent sorts equal priorities stably. Read from source — not yet run — item handling runs before selection, which is the order #4606's toggle needs. AC-3 witnesses it.

The Fix — src/menu/List.mjs

  • One accessor returns a record's cached submenu through store.getKey(record). onKeyDownEnter(), onSelect(), showSubMenu() and toggleSubMenu() all use it.
  • showSubMenu() hides a different active submenu before it shows its own, so at most one submenu per level is mounted. onSelect() reduces to show-or-hide.

Acceptance Criteria

  • AC-1 Enter or click on a parent whose submenu is showing closes it, and the next one opens it again (#4606). Unit spec, for records with and without an id.
  • AC-2 Showing a sibling's submenu unmounts the one showing before it (unit spec).
  • AC-3 A real click opens a parent's submenu on the first click and closes it on the second (component spec — witnesses the listener order).
  • AC-4 Existing menu unit and component specs stay green.

Out of Scope

  • Hover-shown submenus (#18759) and keyboard cascade walking (#18760).

Related

#4606 · #4669 · #9071

Live latest-open sweep: latest 20 open issues at 2026-09-16T08:07:16Z; no equivalent found A2A claim sweep: latest 30 inbox messages, all read-states, at 2026-09-16T08:07Z; no [lane-claim] / [lane-intent] on menus MC sweep: query_raw_memories on the symptom ("menu.List toggleSubMenu onKeyDownEnter subMenuMap; need to click a menu list item to expand the child list"), 6 results, no prior decision; KB ticket query surfaced #4606 Own-assignment sweep: 5 open, none on src/menu

Origin Session ID: fda7faab-19c5-43a0-b192-003966068172 Retrieval Hint: "menu.List submenu cache key space: store.getKey vs getItemRecordId internalId since useInternalId"

tobiu referenced in commit e197300 - "fix(menu): submenu lookups resolve through the record key they are cached by (#18758) (#18761) on Sep 16, 2026, 10:58 AM
tobiu closed this issue on Sep 16, 2026, 10:58 AM