Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/react/src/dialog/root/DialogRoot.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,36 @@ describe('<Dialog.Root />', () => {
});
expect(handleOpenChange.callCount).to.equal(1);
});

it('dismisses when clicking a descendant element within the user backdrop', async () => {
const handleOpenChange = spy();

const { queryByRole } = await render(
<Dialog.Root defaultOpen onOpenChange={handleOpenChange} modal>
<Dialog.Portal>
<Dialog.Backdrop data-testid="backdrop">
<span data-testid="backdrop-child">Child</span>
</Dialog.Backdrop>
<Dialog.Popup />
</Dialog.Portal>
</Dialog.Root>,
);

const backdropChild = screen.getByTestId('backdrop-child');

fireEvent.mouseDown(backdropChild);

expect(queryByRole('dialog')).not.to.equal(null);
expect(handleOpenChange.callCount).to.equal(0);

fireEvent.click(backdropChild);

await waitFor(() => {
expect(queryByRole('dialog')).to.equal(null);
});
expect(handleOpenChange.callCount).to.equal(1);
expect(handleOpenChange.firstCall.args[1].reason).to.equal('outside-press');
});
});

it('waits for the exit transition to finish before unmounting', async ({ skip }) => {
Expand Down
11 changes: 6 additions & 5 deletions packages/react/src/dialog/root/useDialogRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
useInteractions,
useRole,
} from '../../floating-ui-react';
import { getTarget } from '../../floating-ui-react/utils';
import { contains, getTarget } from '../../floating-ui-react/utils';
import { useScrollLock } from '../../utils/useScrollLock';
import { useTransitionStatus, type TransitionStatus } from '../../utils/useTransitionStatus';
import type { RequiredExcept, HTMLProps, FloatingUIOpenChangeDetails } from '../../utils/types';
Expand Down Expand Up @@ -134,14 +134,15 @@ export function useDialogRoot(params: useDialogRoot.Parameters): useDialogRoot.R
}
const target = getTarget(event) as Element | null;
if (isTopmost && dismissible) {
const eventTarget = target as Element | null;
// Only close if the click occurred on the dialog's owning backdrop.
// This supports multiple modal dialogs that aren't nested in the React tree:
// https://github.com/mui/base-ui/issues/1320
if (modal) {
return internalBackdropRef.current || backdropRef.current
? internalBackdropRef.current === eventTarget || backdropRef.current === eventTarget
: true;
const internalBackdrop = internalBackdropRef.current;
const userBackdrop = backdropRef.current;
if (internalBackdrop || userBackdrop) {
return contains(internalBackdrop, target) || contains(userBackdrop, target);
}
}
return true;
}
Expand Down
Loading