Skip to content

Commit 7957866

Browse files
authored
feat: prevent modal from closing when clicking outside (#1177)
Signed-off-by: Mason Hu <[email protected]>
1 parent e588113 commit 7957866

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

src/components/Modal/Modal.stories.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ const meta: Meta<typeof Modal> = {
2929
disable: true,
3030
},
3131
},
32+
33+
closeOnOutsideClick: {
34+
control: "boolean",
35+
},
3236
},
3337
};
3438

@@ -37,7 +41,7 @@ export default meta;
3741
type Story = StoryObj<typeof Modal>;
3842

3943
export const Default: Story = {
40-
render: () => {
44+
render: ({ closeOnOutsideClick }) => {
4145
// eslint-disable-next-line react-hooks/rules-of-hooks
4246
const [modalOpen, setModalOpen] = useState(true);
4347
const closeHandler = () => setModalOpen(false);
@@ -49,6 +53,7 @@ export const Default: Story = {
4953
<Modal
5054
close={closeHandler}
5155
title="Confirm delete"
56+
closeOnOutsideClick={closeOnOutsideClick}
5257
buttonRow={
5358
<>
5459
<button className="u-no-margin--bottom" onClick={closeHandler}>

src/components/Modal/Modal.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,22 @@ describe("Modal ", () => {
171171
await userEvent.click(closeButton!);
172172
expect(handleExternalClick).toHaveBeenCalledTimes(1);
173173
});
174+
175+
it("should not close modal on outside click if closeOnOutsideClick is false", async () => {
176+
const handleCloseModal = jest.fn();
177+
const { container } = render(
178+
<div>
179+
<Modal
180+
title="Test"
181+
close={handleCloseModal}
182+
closeOnOutsideClick={false}
183+
>
184+
Bare bones
185+
</Modal>
186+
</div>,
187+
);
188+
189+
await userEvent.click(container);
190+
expect(handleCloseModal).toHaveBeenCalledTimes(0);
191+
});
174192
});

src/components/Modal/Modal.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export type Props = PropsWithSpread<
2929
* Whether the button click event should propagate.
3030
*/
3131
shouldPropagateClickEvent?: boolean;
32+
/**
33+
* Whether the modal should close when clicking outside the modal.
34+
*/
35+
closeOnOutsideClick?: boolean;
3236
},
3337
HTMLProps<HTMLDivElement>
3438
>;
@@ -45,6 +49,7 @@ export const Modal = ({
4549
close,
4650
title,
4751
shouldPropagateClickEvent = false,
52+
closeOnOutsideClick = true,
4853
...wrapperProps
4954
}: Props): ReactElement => {
5055
// list of focusable selectors is based on this Stack Overflow answer:
@@ -136,7 +141,7 @@ export const Modal = ({
136141
const handleOverlayOnMouseDown = (
137142
event: React.MouseEvent<HTMLDivElement>,
138143
) => {
139-
if (event.target === modalRef.current) {
144+
if (event.target === modalRef.current && closeOnOutsideClick) {
140145
shouldClose.current = true;
141146
}
142147
};

0 commit comments

Comments
 (0)