fix(board): isolate OverflowMenu events from dnd-kit drag listeners#174
fix(board): isolate OverflowMenu events from dnd-kit drag listeners#174ryota-murakami merged 1 commit intomainfrom
Conversation
Move to Maintenance / Move to Another Board / Remove from Board silently no-op'd in production. Symptom: clicking an item in the card's overflow menu would open the Note modal or do nothing at all. Root cause: RepoCard spreads useSortable `attributes` and `listeners` on its wrapper div, turning the entire card into a drag handle. Radix renders DropdownMenuContent inside a Portal, but React still bubbles synthetic events from portal children up through the React tree. So a pointerdown on a menu item reached MouseSensor on the wrapper. With `activationConstraint.distance: 8`, even small cursor jitter between pointerdown and pointerup triggered a drag, after which dnd-kit suppressed the trailing click — and the menu item's onClick never fired. Fix: - OverflowMenu now mounts a `display: contents` wrapper that stops propagation for pointerdown / mousedown / click / keydown so the draggable ancestor never sees menu events. Radix's own handlers run first because they live deeper in the React tree. - RepoCard's handleKeyDown now early-returns when `e.target !== e.currentTarget`, so portal-forwarded keystrokes (e.g. Enter on a focused menu item) can no longer accidentally open the Note modal. Coverage: - OverflowMenu: 4 new "Event Propagation Isolation" tests verify the guard short-circuits ancestor handlers for clicks (trigger + items) and keystrokes. - RepoCard: 1 new test pins the bubbled-Enter early return. - All 194 Board unit tests, typecheck, and lint pass.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughEvent propagation fixes prevent OverflowMenu dropdown interactions and RepoCard keyboard events from bubbling to draggable ancestors (dnd-kit) or triggering unintended modals. Event handlers stop propagation for pointer, mouse, click, and key down events. Regression tests verify isolation and correct callback invocation. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #174 +/- ##
==========================================
+ Coverage 70.13% 70.16% +0.03%
==========================================
Files 161 161
Lines 4654 4659 +5
Branches 1227 1203 -24
==========================================
+ Hits 3264 3269 +5
Misses 1371 1371
Partials 19 19 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
🧪 E2E Coverage Report (Sharded: 12 parallel jobs)
📊 Full report available in workflow artifacts |
Summary
RepoCardspreadsuseSortableattributesandlistenerson its wrapper div, so the entire card is a drag handle. Radix rendersDropdownMenuContentinside a Portal, but React still bubbles synthetic events from portal children up through the React tree. Apointerdownon a menu item therefore reachedMouseSensoron the wrapper. WithactivationConstraint.distance: 8, even small cursor jitter betweenpointerdownandpointerupstarted a drag, and dnd-kit then suppressed the trailingclick— so the menu item'sonClicknever fired.OverflowMenunow mounts adisplay: contentswrapper that stops propagation forpointerdown/mousedown/click/keydown. Radix's own handlers still run first because they live deeper in the React tree, but the draggable ancestor never sees menu events.RepoCard.handleKeyDownearly-returns whene.target !== e.currentTarget, so portal-forwarded keystrokes (e.g.Enteron a focused menu item) can no longer accidentally open the Note modal.Why this design
OverflowMenuinside a drag context gets the fix for free.display: contentskeeps the existing flex layout untouched — no visual diff, no extra spacing.onSelect, the Cancel/Confirm dialog still works.RepoCard: even if a future ancestor forgets to isolate, the card's own shortcuts won't misfire on bubbled events.Test plan
pnpm vitest run src/tests/unit/components/Board/— 194/194 pass (5 new tests pinning the new contract):OverflowMenu› Event Propagation Isolation (dnd-kit guard) (4 tests):display: contentsisolation wrapperMove to Maintenancedoes not bubbleclick/pointerdown/mousedownto a draggable ancestorRepoCard› Keyboard Navigation (1 test):Enterbubbled from a descendant is ignored (noonNotefired)pnpm typecheck— cleanpnpm linton changed files — cleanFiles changed
src/components/Board/OverflowMenu.tsxdisplay: contentsisolation wrappersrc/components/Board/RepoCard.tsxe.target !== e.currentTargetguard inhandleKeyDownsrc/tests/unit/components/Board/OverflowMenu.test.tsxsrc/tests/unit/components/Board/RepoCard.test.tsxSummary by CodeRabbit
Bug Fixes
Tests