Skip to content

fix(Dialog): honor disableOutsidePointerEvents on modal content (#2677)#2679

Merged
zernonia merged 1 commit into
v2from
fix/dialog-modal-disable-outside-pointer-events-2677
Jun 10, 2026
Merged

fix(Dialog): honor disableOutsidePointerEvents on modal content (#2677)#2679
zernonia merged 1 commit into
v2from
fix/dialog-modal-disable-outside-pointer-events-2677

Conversation

@zernonia

@zernonia zernonia commented Jun 6, 2026

Copy link
Copy Markdown
Member

Closes #2677

Problem

DialogContent documents a disableOutsidePointerEvents prop, but passing :disable-outside-pointer-events="false" to a modal dialog had no effect — the body still received pointer-events: none from the DismissableLayer.

Root cause

DialogContentModal hardcoded the value:

<DialogContentImpl
  :disable-outside-pointer-events="true"
  ...
/>

so any value forwarded from DialogContent was overridden.

A naive withDefaults(..., { disableOutsidePointerEvents: true }) on the modal alone is not enough: DialogContent declares the prop as Boolean, so Vue coerces an absent prop to false, which then explicitly overrides the child's true default — breaking the default modal lock. Both "absent" and "explicit false" collapse to false before the modal sees them.

Fix

  1. DialogContentModal: forward props.disableOutsidePointerEvents instead of hardcoding true, with withDefaults(..., { disableOutsidePointerEvents: true }) to preserve the modal default.
  2. DialogContent: declare an explicit undefined default so an absent prop is not boolean-coerced to false and instead propagates as undefined, letting the child apply its own default. An explicit :disable-outside-pointer-events="false" is still honored.

Result (modal dialog, no overlay):

disableOutsidePointerEvents body pointer-events
(absent) none ✅ (default modal lock preserved)
false (unset) ✅ (now honored)
true none

Caveat

When a DialogOverlay is rendered, its useBodyScrollLock(true) sets the body's pointer-events: none through a separate scroll-lock mechanism, independent of this prop. This PR only addresses the DismissableLayer lock that DialogContentModal was 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):

  • locks body pointer-events by default
  • respects disableOutsidePointerEvents=false
  • still locks when explicitly true

All Dialog / Menu / Popover / AlertDialog / DropdownMenu tests pass, and type-check is clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved Dialog component's management of pointer events outside dialog boxes. Modal dialogs now properly apply pointer event blocking settings with better handling of default behavior and explicit configuration options, ensuring more consistent and predictable interaction patterns.

`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>
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Lost in the diff? Review this PR in Change Stack to follow the change map from intent to exact ranges.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e1943cbe-65aa-4033-9507-b296174d6092

📥 Commits

Reviewing files that changed from the base of the PR and between 091d650 and 8445aef.

📒 Files selected for processing (3)
  • packages/core/src/Dialog/Dialog.test.ts
  • packages/core/src/Dialog/DialogContent.vue
  • packages/core/src/Dialog/DialogContentModal.vue

📝 Walkthrough

Walkthrough

The PR fixes issue #2677 where DialogContentModal was overriding the disableOutsidePointerEvents prop passed from DialogContent. By adopting the withDefaults pattern with appropriate defaults and binding to prop values, the prop now propagates correctly. Tests verify the pointer-events behavior across configurations.

Changes

Dialog disableOutsidePointerEvents Prop Propagation

Layer / File(s) Summary
Prop defaults propagation
packages/core/src/Dialog/DialogContent.vue, packages/core/src/Dialog/DialogContentModal.vue
DialogContent uses withDefaults with undefined default to preserve explicit prop values. DialogContentModal uses withDefaults with true default and binds the template to props.disableOutsidePointerEvents instead of a hard-coded constant.
Modal dialog pointer-events test suite
packages/core/src/Dialog/Dialog.test.ts
New makeModalDialog helper and describe('given a modal Dialog (#2677)') suite verify that document.body.style.pointerEvents is locked by default, unlocked when explicitly set to false, and locked when explicitly set to true.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A humble rabbit hops through the code,
Where props now travel the clearest road—
No more overrides hide the way,
Modal defaults let values play!
Tested true with pointer-events care, 🎯

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: honoring the disableOutsidePointerEvents prop on modal content, directly matching the primary change across DialogContent and DialogContentModal.
Linked Issues check ✅ Passed The PR fully implements all objectives from issue #2677: uses withDefaults in DialogContentModal with true as default, removes hardcoded binding, sets undefined default in DialogContent, and adds comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are directly in scope for issue #2677: modifications to DialogContent and DialogContentModal prop defaults, their bindings, and related test coverage. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dialog-modal-disable-outside-pointer-events-2677

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 and usage tips.

@pkg-pr-new

pkg-pr-new Bot commented Jun 6, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/reka-ui@2679

commit: 8445aef

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.

[Bug]: DialogContentModal overrides disableOutsidePointerEvents

1 participant