fix(header): open hover menus from pointerenter instead of mouseenter - #883
Draft
mcstover wants to merge 1 commit into
Draft
fix(header): open hover menus from pointerenter instead of mouseenter#883mcstover wants to merge 1 commit into
mcstover wants to merge 1 commit into
Conversation
Hover-triggered header menus were gated on `navigator.maxTouchPoints`, which browsers report inconsistently for identical hardware — Chrome and Firefox can disagree on the same machine, so whichever reported a non-zero count silently lost hover entirely. Listen on `pointerenter`/`pointerleave` and act only when `pointerType` is `mouse`. The compatibility mouse events a browser synthesizes after a tap are plain MouseEvents, so they never reach a pointer listener at all — a tap that just closed a menu can no longer be reopened by the synthetic event trailing it. This discriminates per event rather than per device, so hybrid touch-and-mouse machines get working hover and working taps. KvHeaderDropdownLink filters before emitting `on-hover`, keeping its contract unchanged; the hamburger and avatar triggers filter in the header components. jsdom ships no PointerEvent, so jest-setup provides a MouseEvent-backed stand-in that preserves `pointerType`.
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.
Problem
Hover-triggered header menus (avatar/MyKiva, Lend, About, Take action) were gated on
navigator.maxTouchPoints:Browsers report that value inconsistently for identical hardware — Chrome and Firefox can disagree on the same machine — so on whichever browser reports a non-zero count, the hover menus silently stop opening altogether.
maxTouchPointsis a raw hardware capability count, not a statement about how the user is currently interacting. It was standing in for a question it can't answer.Approach
Listen on
pointerenter/pointerleaveand act only whenevent.pointerType === 'mouse'.The reason this works is in how browsers sequence a tap. Dispatching a real touch and logging every event gives:
There is exactly one
pointerenter, and it always carriespointerType: "touch". The compatibility events browsers synthesize afterwards are plainMouseEvents with nopointerType— so they never reach apointerenterlistener at all. A tap that just closed a menu can't be reopened by the synthetic event trailing it, with no timers and no device sniffing.Because this discriminates per event rather than per device, hybrid touch-and-mouse machines (Surface, touchscreen laptops) get working hover and working taps — a device-level check can't do both.
Changes
KvHeaderDropdownLink.vue— filters onpointerTypebefore emittingon-hover, so the emitted contract is unchanged for both header hosts. Shared by both headers, covers Lend / About / Take action.KvWwwHeader/KvHeaderLinkBar.vueandKvWwwHeaderBasic/LinkBar.vue— hamburger and avatar triggers filter viahandlePointerHover/handlePointerOut; bothmaxTouchPointsguards removed.handleTouchStartand the tap paths are untouched.tests/unit/jest-setup.js— jsdom ships noPointerEvent, sofireEvent.pointerEnter()silently dropspointerType. Adds a minimalMouseEvent-backed stand-in.Tests
New
KvHeaderLinkBar.spec.ts(the component had no spec) plus additions toLinkBar.spec.ts, covering: a real mouse hover opens the menu; a baremouseenter(what a browser fires after a tap) does not; a touch-typepointerenterdoes not; tapping still opens.Verified these fail before the fix — the compatibility-
mouseentertest reproduces the double-toggle onmain— and that removing thepointerTypeguards fails 4 of them.npm run test(lint + jest): 65 suites, 553 tests passing.Verification notes
The event sequence above was captured from Chrome via CDP with real dispatched touch events, since JS-dispatched events don't generate compatibility mouse events. Worth a confirmation pass in Firefox on a machine that reproduced the original bug before this comes out of draft.