Skip to content

fix(menu): keep a click on a menu item inside its own panel - #8685

Closed
talissoncosta wants to merge 1 commit into
mainfrom
01-menu-click-containment
Closed

talissoncosta wants to merge 1 commit into
mainfrom
01-menu-click-containment

Conversation

@talissoncosta

@talissoncosta talissoncosta commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Contributes to #8465

1/5, splitting #8613 into reviewable pieces.

A menu portalled to the body is not a DOM descendant of whatever opened it, so a click on one of its items reaches the outside-click watcher of the panel it was opened from, and that panel closes on the very item you picked. useContainClicks stops the click at the menu.

Native listeners rather than React's onMouseUp, because those sit below document, which is where useOutsideClick listens.

No panel on main currently holds a DropdownMenu, so there is nothing to reproduce today. This lands ahead of the tag panel, which does.

How did you test this code?

Typecheck at main's baseline, 916 errors both sides.

Regression only. Each of the five DropdownMenu call sites should still open, fire an item, and close on both Escape and an outside click:

  1. Integrations list, an integration's menu
  2. Experiment detail, the action dropdown
  3. A tabbed page, the tab overflow menu
  4. Feature lifecycle, the stale section menu
  5. Release pipelines list, a pipeline's menu

A menu portalled to the body is not a DOM descendant of whatever opened
it, so a click on an item reached the outside-click watchers and closed
the panel on the very item you picked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@talissoncosta
talissoncosta requested a review from a team as a code owner October 6, 2026 14:20
@talissoncosta
talissoncosta requested review from kyle-ssg and removed request for a team October 6, 2026 14:20
@vercel

vercel Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
flagsmith-frontend-preview Ready Ready Preview Oct 6, 2026 2:21pm UTC
flagsmith-frontend-staging Ready Ready Preview Oct 6, 2026 2:21pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
docs Ignored Ignored Oct 6, 2026 2:21pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 41234bfc-d10f-42a3-96fb-ca6ec2093929
📥 Commits

Reviewing files that changed from the base of the PR and between a1522da and a8455cf.

📒 Files selected for processing (2)
  • frontend/common/useContainClicks.ts
  • frontend/web/components/base/DropdownMenu.tsx

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The change adds the useContainClicks hook. When active and the referenced element exists, the hook stops propagation of mouseup and touchend events and removes its listeners during cleanup. DropdownMenu uses the hook with its dropdown ref and open state.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to a8455

The dropdown containment change is ready to merge after normal checks.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added front-end Issue related to the React Front End Dashboard fix labels Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-8685 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-8685 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith:pr-8685 Finished ✅ Results ✅
ghcr.io/flagsmith/flagsmith-api:pr-8685 Finished ✅ Results ✅
ghcr.io/flagsmith/flagsmith-private-cloud:pr-8685 Finished ✅ Results ✅
ghcr.io/flagsmith/flagsmith-frontend:pr-8685 Finished ✅ Results ✅

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor
✅ private-cloud · depot-ubuntu-latest-arm-16 — run #21246 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  1 minute
commit  a8455cf
info  🔄 Run: #21246 (attempt 1)

🗂️ Previous results
✅ private-cloud · depot-ubuntu-latest-16 — run #21246 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  3 passed

Details

stats  3 tests across 3 suites
duration  49.9 seconds
commit  a8455cf
info  🔄 Run: #21246 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #21246 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  35.3 seconds
commit  a8455cf
info  🔄 Run: #21246 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #21246 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  31.4 seconds
commit  a8455cf
info  🔄 Run: #21246 (attempt 1)

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Visual Regression

20 screenshots compared. See report for details.
View full report

@talissoncosta

Copy link
Copy Markdown
Contributor Author

@themis-blindfold review

const node = ref.current
if (!active || !node) return
const stop = (e: Event) => e.stopPropagation()
node.addEventListener('mouseup', stop)

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.

🟠 Major · ⚡ Quick win

Add a regression test for the containment contract.

Observed: This hook changes mouseup and touchend propagation, but neither it nor DropdownMenu has automated coverage. Predicted: A later change to the portal, event type, or listener phase could reintroduce the modal-closing bug without CI detecting it. Cover an InlineModal with a portalled DropdownMenu, asserting a menu selection keeps the modal open while a real outside click closes it.

@themis-blindfold

Copy link
Copy Markdown
Contributor

⚖️ Themis review: 🟠 Fix before merge

TL;DR: The containment itself is aligned with the document-level outside-click listener and completed CI checks are successful or neutral. The new propagation contract has no regression test, so the modal-closing case this change is intended to prevent can silently return.

Area Score
🎯 Correctness 3/5
🧪 Test coverage 1/5
📐 Code quality 4/5
🚀 Product impact 3/5

🟠 Majors

  • frontend/common/useContainClicks.ts:20 — add regression coverage for the portalled-menu containment contract.
📝 Walkthrough
  • useContainClicks - introduces native mouse and touch event containment for portalled content.
  • DropdownMenu - enables containment only while its portal is mounted, preserving regular outside-click dismissal.
🧪 How to verify
  1. Add a regression test mounting a DropdownMenu inside an InlineModal and select a menu action.
  2. Assert that selecting the action leaves the parent modal open, while a mouse or touch interaction outside it still closes it.
  3. Manually exercise the tag picker’s Edit action once its consumer lands, including on a touch device.
  4. Run cd frontend && npm run test:unit.

Automate: Keep the modal-plus-portalled-menu interaction in the unit suite.

Product take: This is useful groundwork for the accessible tag panel, but its user-facing benefit depends on an event-ordering edge case that needs a durable test.

🧭 Assumptions & unverified claims

No unverified assumptions or claims.

A small portal tether, but it needs a regression test before it becomes a safety rope · reviewed at a8455cf

@talissoncosta

Copy link
Copy Markdown
Contributor Author

Closing. This is not a bug on main.

The failure needs one panel's outside-click watcher wrapping a different component's portalled menu. Every component on main that both watches outside clicks and portals (UserAction, AccountDropdown, DropdownMenu itself) attaches its ref to the portalled node:

useOutsideClick(dropDownRef, onOutsideClick)
return createPortal(<div ref={dropDownRef} …>, …)

Watcher and content are the same subtree, so a click inside is never outside. No nesting, nothing to fix.

The only place the condition arises is the new tag panel, which puts a DropdownMenu inside an InlineModal for the per-tag actions. main's panel has no menu. So useContainClicks has moved into that PR, where the code that needs it lives.

#8613 listed this under "pre-existing bugs fixed on the way", which was wrong: the panel introduces the condition and fixes it in the same change.

@talissoncosta
talissoncosta deleted the 01-menu-click-containment branch October 6, 2026 14:39

This branch was successfully deployed

2 active deployments
Preview – flagsmith-frontend-preview — a8455cfd Deployed Oct 6, 2026 by vercel[bot]
Preview – flagsmith-frontend-staging — a8455cfd Deployed Oct 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants