fix(Dialog): restore focus to trigger after overlay click#2655
Conversation
Clicking the overlay fires `pointerdown`, which dismisses the dialog and triggers focus restoration to the trigger. The browser then dispatches the compatibility `mousedown` event, but the overlay is already unmounted so `mousedown` fires on `<body>` and blurs the just-focused trigger — focus falls to body. Prevent the default on `pointerdown` (primary button only) so the browser skips the compatibility `mousedown`. Right-click still works because the `.left` modifier scopes the prevention to button 0. Fixes #2649 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 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 |
|
Hi @zernonia, I think your fix broke the usage with |
#2655 added `@pointerdown.left.prevent` on the overlay so focus stays on the trigger after the dialog closes. When `DialogContent` is nested inside `DialogOverlay` (a common centering pattern), that handler also fired for pointerdown events bubbling up from controls inside the content, calling `preventDefault()` on them. That suppressed native behavior — `<select>` dropdowns wouldn't open and `<input>`/`<textarea>` wouldn't focus on click. Add the `.self` modifier so the prevention only applies when the pointer lands directly on the overlay backdrop, preserving the #2655 behavior while leaving descendant interactions untouched. Fixes #2660 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lf (#2668) #2655 added `@pointerdown.left.prevent` on the overlay so focus stays on the trigger after the dialog closes. When `DialogContent` is nested inside `DialogOverlay` (a common centering pattern), that handler also fired for pointerdown events bubbling up from controls inside the content, calling `preventDefault()` on them. That suppressed native behavior — `<select>` dropdowns wouldn't open and `<input>`/`<textarea>` wouldn't focus on click. Add the `.self` modifier so the prevention only applies when the pointer lands directly on the overlay backdrop, preserving the #2655 behavior while leaving descendant interactions untouched. Fixes #2660 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🔗 Linked issue
closes #2649
❓ Type of change
📚 Description
When the dialog is closed via the overlay click, focus falls to
<body>instead of returning to the trigger. Escape and the close button work fine; only the overlay click is affected.Root cause: the sequence is
pointerdown→dismiss→DialogContentunmounts →FocusScoperestores focus to the trigger. The browser then dispatches the compatibilitymousedownevent, but the overlay is already gone, somousedownfires on<body>. The browser's defaultmousedownbehavior on a non-focusable element blurs the just-focused trigger — focus falls to body.Fix: add
@pointerdown.left.preventtoDialogOverlayImpl. Per the Pointer Events spec, preventing default onpointerdownsuppresses the corresponding compatibilitymousedown, so no blur happens. The.leftmodifier scopes the prevention to primary button so right-click context menus still work.🧪 Verification
Reproduced the bug in headless Chromium against
docs/components/dialog:After the fix,
mousedownis suppressed and focus stays on the trigger.Manually verified in the browser:
Added a regression test for the overlay-click path; full suite (1,635 tests) passes.
🤖 Generated with Claude Code