Skip to content

fix(application-shell): let clicks on navigation menu labels reach their link (FEC-1479) - #4224

Draft
fparadelact wants to merge 1 commit into
mainfrom
fix/navbar-safe-triangle-clicks
Draft

fparadelact wants to merge 1 commit into
mainfrom
fix/navbar-safe-triangle-clicks

Conversation

@fparadelact

@fparadelact fparadelact commented Oct 30, 2026 •

Copy link
Copy Markdown

Summary

Clicking the label of a left navigation menu item that has a submenu (Products, Categories) often only opens the submenu instead of navigating. The submenu's safe area, the invisible triangle that keeps the submenu open while the pointer travels to it, starts exactly under the pointer and takes the release of the click. This starts the triangle 2px off the pointer, on the side away from the submenu.

 // navbar.tsx, the layout effect that writes the triangle's tip
-  --safe-start: X% Y%                  // tip under the pointer
+  --safe-start: X% calc(Y% + 2px)      // submenu opens downward
+  --safe-start: X% calc(Y% - 2px)      // submenu opens upward

Observed behavior

  • pointerdown lands on the label, inside the <a>. Hovering has opened the submenu, and the tip is written at the pointer's position.
  • pointerup lands on the SafeArea span, outside the <a>. The click goes to their common ancestor, so the NavLink never gets it.
  • It is a race. The tip comes from mousemove's whole-pixel clientX/Y, while pointer events keep the fraction, so the pointer sits a fraction of a pixel right of and below the tip. The click is lost when that fraction falls inside the triangle's corner, or when it is zero. If the last mousemove came before the submenu settled, the tip is stale and the click succeeds.
  • Downward submenus have a steep lower edge, so most clicks are lost. Settings opens upward with an almost flat edge, so it rarely happens there.

The race is why a test can miss it.

Expected behavior

A click on the label always navigates. The submenu stays open on every path into it, including diagonals across other menu items.

What changed

  • The tip moves 2px off the pointer, using the component's existing isSubmenuAboveMenuItem state for the direction.
  • px, not %: 1% is under 1px for a submenu shorter than 100px, and a sub-pixel offset could reach the tip again.
  • Away from the submenu, not always below: a fixed offset below makes every upward step climb 2px further to stay in the previous triangle. Measured on staging, it loses the slow path from Settings to its first entry. Away from the submenu, no path was lost.
  • Tried and discarded: pointer-events: none while over the item (loses every diagonal, as Michael found in fix(application-shell): keep the left navigation scrollable over submenus (FEC-1449) #4204), the tip 8px right of the pointer (loses slow diagonals), the tip at the item's right edge (loses every diagonal).

Testing

  • Unit tests: --safe-start for a downward and an upward submenu. jsdom does no hit-testing, so they cover the value, not the click.

  • Playwright against the playground built from this branch (Chromium, 1920×1080). It presses the label at sub-pixel offsets from a fresh tip, and moves from the label to every submenu entry and straight right, slow and fast. The upward case pushes the menu item down so the submenu flips.

    Before After
    Clicks that navigate, submenu down 3/12 12/12
    Clicks that navigate, submenu up 6/12 12/12
    Paths that keep the submenu open, down 14/14 14/14
    Paths that keep the submenu open, up 14/14 14/14

    The click offsets are chosen to probe the triangle's corner, so the before column is not a real-world rate.

  • On staging, by emulation (the probe rewrote --safe-start in the page): the same offset kept all 44 paths into the submenus of Products, Categories and Settings, and all 6 diagonals across a neighbouring menu item. On a straight move to the neighbour, the neighbour still took over (2/2).

  • By hand in the playground: the label always navigates. The submenu stays open while the pointer is over the item or moves towards the submenu.

…eir link

The submenu's safe area started its triangle exactly under the pointer, so
when the submenu opened on hover, the release of a click on the menu item
often landed on the safe area instead of the label's link, and the click
only opened the submenu. Start the triangle 2px off the pointer, on the side
away from the submenu, so the label stays clickable and every path into the
submenu still crosses the triangle.
@vercel

vercel Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
mc-app-kit-playground Ready Ready Preview Oct 30, 2026 11:56am UTC
merchant-center-application-kit-components-playground Ready Ready Preview Oct 30, 2026 11:56am UTC

Request Review

@changeset-bot

changeset-bot Bot commented Oct 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3b08e86

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 29 packages
Name Type
@commercetools-frontend/application-shell Patch
@commercetools-frontend/actions-global Patch
@commercetools-frontend/application-components Patch
@commercetools-frontend/application-config Patch
@commercetools-frontend/application-shell-connectors Patch
@commercetools-frontend/assets Patch
@commercetools-frontend/babel-preset-mc-app Patch
@commercetools-frontend/browser-history Patch
@commercetools-frontend/codemod Patch
@commercetools-frontend/constants Patch
@commercetools-frontend/create-mc-app Patch
@commercetools-frontend/cypress Patch
@commercetools-frontend/eslint-config-mc-app Patch
@commercetools-frontend/i18n Patch
@commercetools-frontend/jest-preset-mc-app Patch
@commercetools-frontend/jest-stylelint-runner Patch
@commercetools-frontend/l10n Patch
@commercetools-frontend/mc-dev-authentication Patch
@commercetools-frontend/mc-html-template Patch
@commercetools-frontend/mc-scripts Patch
@commercetools-frontend/notifications Patch
@commercetools-frontend/permissions Patch
@commercetools-frontend/react-notifications Patch
@commercetools-frontend/sdk Patch
@commercetools-frontend/sentry Patch
@commercetools-frontend/url-utils Patch
@commercetools-backend/eslint-config-node Patch
@commercetools-backend/express Patch
@commercetools-backend/loggers Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.95%. Comparing base (4f0331d) to head (3b08e86).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4224      +/-   ##
==========================================
+ Coverage   75.44%   75.95%   +0.50%     
==========================================
  Files         297      297              
  Lines        7713     7714       +1     
  Branches     2435     2396      -39     
==========================================
+ Hits         5819     5859      +40     
+ Misses       1873     1834      -39     
  Partials       21       21              
Components Coverage Δ
Application Components 81.18% <ø> (ø)
Application Shell 80.24% <100.00%> (+1.55%) ⬆️
Application Shell Connectors 77.54% <ø> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4f0331d...3b08e86. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fparadelact fparadelact changed the title fix(application-shell): let clicks on navigation menu labels reach their link fix(application-shell): let clicks on navigation menu labels reach their link (FEC-1479) Oct 30, 2026
Comment on lines +231 to +234
submenuRef.current.style.setProperty(
'--safe-start',
`${safeX}% calc(${safeY}% ${isSubmenuAboveMenuItem ? '-' : '+'} 2px)`
);

@misama-ct misama-ct Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the fix works. In my browser test, label clicks now reach the link: 0/60 before, 59/60 with this PR, including clicks at sub-pixel positions.

I found one side effect, in the collapsed navbar, and I suggest a small change before merging.

Collapsed navbar

In the collapsed navbar, the submenu starts at the bottom of the menu item (menuItemBottom). So the pointer on an icon is never inside the safe area, and clicks on icons were never lost.

But the pointer's Y is then clamped to 0% (or 100% when the submenu opens upward), and the + 2px moves the tip into the safe area. This leaves a thin band right below the hovered icon that the triangle no longer covers. When the mouse passes through that band on its way to the submenu, the next icon takes the hover and the submenu switches.

Suggestion: add the offset only when the pointer is inside the safe area.

// When the pointer is above or below the safe area (collapsed navbar), Y is
// clamped to 0% or 100%. An offset there would open a gap along that edge.
const isPointerInsideSafeArea = safeY > 0 && safeY < 100;
const startY = isPointerInsideSafeArea
  ? `calc(${safeY}% ${isSubmenuAboveMenuItem ? "-" : "+"} 2px)`
  : `${safeY}%`;
submenuRef.current.style.setProperty("--safe-start", `${safeX}% ${startY}`);

With this change, the existing tests in navbar.spec.tsx still pass. A test for the clamped case would be good, for example: pointer above the safe area, so --safe-start has no offset.

Merchant Center

No code in merchant-center-frontend depends on these internals (sometimes devs target internals). The Cypress command navigateToMenu force-clicks the submenu link, so the e2e tests do not depend on where the label click lands.

This branch was successfully deployed

2 active deployments
Preview – mc-app-kit-playground — 3b08e86e Deployed Oct 30, 2026 by vercel[bot]
Preview – merchant-center-application-kit-components-playground — 3b08e86e Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants