NMS-20273: Open side menu submenus on hover, in both rail states - #8831
Merged
synqotik merged 2 commits intoSep 1, 2026
Merged
Conversation
Hovering a top-level entry now flies its submenu out instead of requiring a click. What the rail never does on hover is change its own width: an earlier iteration expanded it from under the pointer, and that — rather than the flyouts — was the jarring part. TieredMenu only opens submenus on hover once its internal `dirty` flag is set, normally by a first click. Setting it the moment the pointer touches the rail makes flyouts fire at every entry the pointer merely passes over, so the first entry has to be dwelled on for 150ms: a delegated mouseover starts the timer, and when it elapses we set `dirty` and re-dispatch mouseenter on the item (TieredMenu binds it on the item's content wrapper, and mouseenter does not bubble) so PrimeVue's own handler does the opening. After that the rail is in hover mode and TieredMenu switches between entries itself, with no further dwell, until the pointer leaves. Leaving closes any open flyout after a 200ms grace, since a clamped flyout can take the pointer briefly outside the rail on its way there. Collapsed, entries that are direct links (Topology, the maps) have no submenu, so they get their label tooltip on the same 150ms beat instead of the previous 300ms; entries that do have a submenu no longer show one, the flyout being the affordance. Expanded, labels are already visible and tooltips stay suppressed. The tests render transitions for real: VTU stubs them by default, which parks the root list's class on the stub rather than the <ul>, so the '.p-tieredmenu-root-list > .p-tieredmenu-item' selector the rail uses in production would match nothing.
synqotik
marked this pull request as draft
September 1, 2026 01:39
Three fixes, all in the hover teardown path, plus one shared helper for it. A pending dwell was not cancelled when the pointer moved onto the rail's own chrome. closest() returns null for the toggle button and the gap below the last entry, and the handler returned early without clearing the timer or hoveredItem — so the timer's only guard (hoveredItem !== item) still held and it fired, opening a flyout for an entry the pointer had already left. Reaching for the collapse toggle after hovering an entry is the common case. Clearing hoveredItem there also re-arms the dwell when the pointer comes back to that same entry, which the old code left inert. togglePinned left `dirty` set, contradicting its own comment. TieredMenu never clears it when the active path empties, which happens whenever a direct link (Topology, the maps) is hovered in hover mode, so the activeItemPath guard skipped hide() and the rail stayed in hover mode across the toggle — every subsequent hover opening with no dwell. The rail's mouseleave discarded keyboard state: hide() resets focusedItemInfo, and it ran whether the flyout had been hovered open or clicked/tabbed into. Hover never focuses the menubar (verified in a browser: a hover leaves activeElement on <body>, a click moves it to the menubar <ul>), so `focused` cleanly separates the two. When the menu holds focus, the focused index is now put back on the root entry that was open — where TieredMenu itself lands after closing a submenu. A deeper position is deliberately not restored as-is: it points into a submenu that is now closed, and TieredMenu would resolve the stale index against the root list. The close-and-leave-hover-mode logic was duplicated between togglePinned and the mouseleave timer; both now call one closeFlyouts() helper. Reviewed and not changed: `hasSubmenu` in the #item slot. TieredMenuSub binds it as :hasSubmenu="!!getItemProp(processedItem, 'items')" — the double-bang is on the slot binding, so it is already a boolean and matches the .d.ts. The two tooltip assertions use toBe(), which is Object.is: an items array would not equal true, nor undefined false.
synqotik
marked this pull request as ready for review
September 1, 2026 02:04
synqotik
requested review from
indigo423,
marshallmassengill and
mershad-manesh
September 1, 2026 02:04
synqotik
deleted the
jira/NMS-20273-sidemenu-hover-flyout-when-collapsed-smoke
branch
September 1, 2026 14:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
NMS-20273: Open side menu submenus on hover, in both rail states
Hovering a top-level entry in the side menu now flies its submenu out, instead of requiring a
click. This applies whether the rail is collapsed or pinned open.
Background
The rail used to expand on hover and open its flyouts (NMS-18287). NMS-20167 removed both,
because the combination was jarring, leaving click-to-open. That turned out to be dialed back too
far: the jarring part was the rail resizing from under the pointer — reflowing the page and moving
every icon the user was aiming at — not the flyouts themselves.
So the flyouts come back, and the rail's width does not move.
isPinnedis now the only thing thatsets it, and only the toggle button and
Ctrl+\changeisPinned.Behavior
instantly, with no further delay — the macOS menu-bar feel.
re-entering needs a fresh dwell.
navigation.
The dwell is the point of the design. Opening on bare
mouseenterfires a flyout at every entrythe pointer merely crosses on its way somewhere else, which is the same class of noise the old
hover mode had. 150ms is long enough to ignore a pass-through and short enough to read as instant
on a deliberate hover.
The close grace matters because a flyout is not always beside its own entry:
positionFlyoutsclamps tall flyouts to the rail's vertical band, so reaching one can take the pointer briefly
outside the rail. Closing on the first
mouseleavewould cut that travel off.Tooltips
While collapsed, top-level entries had a label tooltip, since their labels are hidden. Now:
keep theirs, and its
showDelaydrops from 300ms to the same 150ms as the flyout, so everytop-level entry responds on the same beat regardless of which kind it is.
Notes for reviewers
dirtyflag, and a synthetic event aimed at aPrimeVue-owned element. A PrimeVue upgrade could change either. It is contained to this one
component, which already carries a comment explaining why TieredMenu is used unwrapped rather
than behind an
onms-uiseam component, now extended to cover this. The alternative — callingonItemChange()with a processed item pulled off the instance — couples to more internals, notfewer, and skips PrimeVue's own handler.
until an outside click. Deliberate: the interaction should not differ between the two states.
definitions.
External References