fix(Dialog): honor disableOutsidePointerEvents on modal content (#2677)#2679
Conversation
`DialogContentModal` hardcoded `:disable-outside-pointer-events="true"`, so a
`disableOutsidePointerEvents` value passed to `DialogContent` was silently
ignored even though the docs list it as a prop.
Pass the prop through with `withDefaults(..., { disableOutsidePointerEvents:
true })` so modal dialogs stay locked by default but can be overridden. Because
`DialogContent` declares the prop as `Boolean`, Vue coerces an absent value to
`false`, which would override the child default; declare an explicit `undefined`
default on `DialogContent` so the absent case propagates as `undefined` and the
child's default applies, while an explicit `false` is still honored.
Note: when a `DialogOverlay` is rendered, its `useBodyScrollLock` locks the
body's `pointer-events` through a separate mechanism; this change only affects
the `DismissableLayer` lock.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Lost in the diff? Review this PR in Change Stack to follow the change map from intent to exact ranges. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe PR fixes issue ChangesDialog disableOutsidePointerEvents Prop Propagation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes 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 |
commit: |
Closes #2677
Problem
DialogContentdocuments adisableOutsidePointerEventsprop, but passing:disable-outside-pointer-events="false"to a modal dialog had no effect — the body still receivedpointer-events: nonefrom theDismissableLayer.Root cause
DialogContentModalhardcoded the value:so any value forwarded from
DialogContentwas overridden.A naive
withDefaults(..., { disableOutsidePointerEvents: true })on the modal alone is not enough:DialogContentdeclares the prop asBoolean, so Vue coerces an absent prop tofalse, which then explicitly overrides the child'struedefault — breaking the default modal lock. Both "absent" and "explicit false" collapse tofalsebefore the modal sees them.Fix
DialogContentModal: forwardprops.disableOutsidePointerEventsinstead of hardcodingtrue, withwithDefaults(..., { disableOutsidePointerEvents: true })to preserve the modal default.DialogContent: declare an explicitundefineddefault so an absent prop is not boolean-coerced tofalseand instead propagates asundefined, letting the child apply its own default. An explicit:disable-outside-pointer-events="false"is still honored.Result (modal dialog, no overlay):
disableOutsidePointerEventspointer-eventsnone✅ (default modal lock preserved)falsetruenone✅Caveat
When a
DialogOverlayis rendered, itsuseBodyScrollLock(true)sets the body'spointer-events: nonethrough a separate scroll-lock mechanism, independent of this prop. This PR only addresses theDismissableLayerlock thatDialogContentModalwas overriding — matching the exact issue reported. Freeing the body while an overlay/scroll-lock is active would be a separate change with broader impact (shared by Select/Combobox/Menu/Popover).Tests
Added tests under
Dialog.test.ts(modal dialog, no overlay):pointer-eventsby defaultdisableOutsidePointerEvents=falsetrueAll Dialog / Menu / Popover / AlertDialog / DropdownMenu tests pass, and
type-checkis clean.🤖 Generated with Claude Code
Summary by CodeRabbit