Skip to content

Commit 842836f

Browse files
Add shouldCloseOnInteractOutside in <DialogTrigger /> (#292)
1 parent 4fb5c07 commit 842836f

File tree

5 files changed

+70
-1
lines changed

5 files changed

+70
-1
lines changed

.changeset/late-fans-tap.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@cube-dev/ui-kit': patch
3+
---
4+
5+
Added new prop in `<DialogTrigger />` - `shouldCloseOnInteractOutside`, which gives you a chance to filter out interaction with elements that should not dismiss the overlay.

src/components/overlays/Dialog/DialogTrigger.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export interface CubeDialogTriggerProps extends WithCloseBehavior {
4141
mobileViewport?: number;
4242
/** The style map for the overlay **/
4343
styles?: Styles;
44+
shouldCloseOnInteractOutside?: (element: Element) => boolean;
4445
}
4546

4647
function DialogTrigger(props) {
@@ -56,6 +57,7 @@ function DialogTrigger(props) {
5657
styles,
5758
mobileViewport = 700,
5859
hideOnClose,
60+
shouldCloseOnInteractOutside,
5961
...positionProps
6062
} = props;
6163

@@ -117,6 +119,7 @@ function DialogTrigger(props) {
117119
content={content}
118120
isKeyboardDismissDisabled={isKeyboardDismissDisabled}
119121
hideArrow={hideArrow}
122+
shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}
120123
onClose={onClose}
121124
/>
122125
);
@@ -136,6 +139,7 @@ function DialogTrigger(props) {
136139
type={type}
137140
isKeyboardDismissDisabled={isKeyboardDismissDisabled}
138141
styles={styles}
142+
shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}
139143
onClose={onClose}
140144
onExiting={onExiting}
141145
onExited={onExited}
@@ -188,6 +192,7 @@ function PopoverTrigger(allProps) {
188192
onClose,
189193
isKeyboardDismissDisabled,
190194
hideOnClose,
195+
shouldCloseOnInteractOutside,
191196
...props
192197
} = allProps;
193198

@@ -232,6 +237,7 @@ function PopoverTrigger(allProps) {
232237
isKeyboardDismissDisabled={isKeyboardDismissDisabled}
233238
hideArrow={hideArrow}
234239
updatePosition={updatePosition}
240+
shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}
235241
onClose={onClose}
236242
>
237243
{typeof content === 'function' ? content(state.close) : content}

src/components/overlays/Dialog/stories/Dialog.stories.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { within, userEvent, waitFor } from '@storybook/testing-library';
22
import { expect } from '@storybook/jest';
33
import { Story } from '@storybook/react';
4+
import { useRef, useState } from 'react';
5+
import { FocusableRefValue } from '@react-types/shared';
46

57
import { CubeDialogProps } from '../Dialog';
68
import { CubeDialogTriggerProps } from '../DialogTrigger';
@@ -14,6 +16,7 @@ import {
1416
Header,
1517
Paragraph,
1618
Title,
19+
Space,
1720
} from '../../../../index';
1821
import { baseProps } from '../../../../stories/lists/baseProps';
1922

@@ -215,3 +218,54 @@ CloseOnOutsideClick.play = async (context) => {
215218

216219
expect(dialog).not.toBeInTheDocument();
217220
};
221+
222+
export const DoNotCloseOnClickAtParticularElement: typeof Template = () => {
223+
const btnRef = useRef<FocusableRefValue<HTMLButtonElement>>(null);
224+
const [itWorks, setItWorks] = useState(false);
225+
226+
return (
227+
<>
228+
<Space gap="2x">
229+
<DialogTrigger
230+
type="popover"
231+
shouldCloseOnInteractOutside={(e) =>
232+
btnRef.current?.UNSAFE_getDOMNode() !== e
233+
}
234+
>
235+
<Button size="small">Open modal</Button>
236+
237+
<Dialog size="small">
238+
<Header>
239+
<Title>Modal title</Title>
240+
</Header>
241+
<Content>
242+
<Paragraph>Test content</Paragraph>
243+
<Paragraph>Test content</Paragraph>
244+
</Content>
245+
</Dialog>
246+
</DialogTrigger>
247+
248+
<Button ref={btnRef} size="small" onPress={() => setItWorks(true)}>
249+
{itWorks ? 'It works!' : 'Click me!'}
250+
</Button>
251+
</Space>
252+
</>
253+
);
254+
};
255+
DoNotCloseOnClickAtParticularElement.play = async (context) => {
256+
if (context.viewMode === 'docs') return;
257+
258+
const { findByRole } = within(context.canvasElement);
259+
260+
const trigger = await findByRole('button', { name: 'Open modal' });
261+
const button = await findByRole('button', { name: 'Click me!' });
262+
263+
await userEvent.click(trigger);
264+
265+
await expect(await findByRole('dialog')).toBeInTheDocument();
266+
267+
await userEvent.click(button);
268+
269+
await expect(await findByRole('dialog')).toBeInTheDocument();
270+
await expect(button).toHaveTextContent('It works!');
271+
};

src/components/overlays/Modal/Modal.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,14 @@ export interface CubeModalProps
7777
onClose?: (action?: string) => void;
7878
type?: 'modal' | 'fullscreen' | 'fullscreenTakeover';
7979
styles?: Styles;
80+
shouldCloseOnInteractOutside?: (element: Element) => boolean;
8081
}
8182

8283
function Modal(props: CubeModalProps, ref) {
8384
let { qa, children, onClose, type, styles, ...otherProps } = props;
8485
let domRef = useDOMRef(ref);
8586

86-
let { overlayProps, underlayProps } = useOverlay(props, domRef);
87+
let { overlayProps, underlayProps } = useOverlay({ ...props }, domRef);
8788

8889
return (
8990
<Overlay {...otherProps}>

src/components/overlays/Modal/Popover.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export interface CubePopoverProps
5252
isNonModal?: boolean;
5353
isDismissable?: boolean;
5454
updatePosition?: () => void;
55+
shouldCloseOnInteractOutside?: (element: Element) => boolean;
5556
}
5657

5758
function Popover(props: CubePopoverProps, ref) {
@@ -68,6 +69,7 @@ function Popover(props: CubePopoverProps, ref) {
6869
isNonModal,
6970
isDismissable = true,
7071
updatePosition,
72+
shouldCloseOnInteractOutside,
7173
...otherProps
7274
} = props;
7375

@@ -85,6 +87,7 @@ function Popover(props: CubePopoverProps, ref) {
8587
isNonModal={isNonModal}
8688
isDismissable={isDismissable}
8789
updatePosition={updatePosition}
90+
shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}
8891
onClose={onClose}
8992
>
9093
{children}

0 commit comments

Comments
 (0)