(react) add closeOnEsc props to Modal

We want to be able to disable closing modals by pressing escape in
some cases.
This commit is contained in:
Nathan Vasse
2024-04-23 12:01:47 +02:00
committed by NathanVss
parent 2916dd2af9
commit 285cf99681
4 changed files with 63 additions and 1 deletions

View File

@@ -149,6 +149,12 @@ You can change this behavior by passing the `closeOnClickOutside` prop.
<Canvas of={Stories.CloseOnClickOutside} story={{inline: false}}/> <Canvas of={Stories.CloseOnClickOutside} story={{inline: false}}/>
## Close on escape
By default, the modal will be closed when you press the `esc` key. You can change this behavior by passing the `closeOnEsc` prop.
<Canvas of={Stories.DontCloseOnEsc} story={{inline: false}}/>
## Pre Built Modals ## Pre Built Modals
As we know that developers love to have handy shortcuts for common use cases, we provide some pre built modals that we As we know that developers love to have handy shortcuts for common use cases, we provide some pre built modals that we

View File

@@ -209,6 +209,54 @@ describe("<Modal/>", () => {
await user.click(modal); await user.click(modal);
expect(screen.queryByText("Modal Content")).toBeInTheDocument(); expect(screen.queryByText("Modal Content")).toBeInTheDocument();
}); });
it("close on esc by default", async () => {
const Wrapper = () => {
const modal = useModal();
return (
<CunninghamProvider>
<button onClick={modal.open}>Open Modal</button>
<Modal size={ModalSize.SMALL} {...modal}>
<div>Modal Content</div>
</Modal>
</CunninghamProvider>
);
};
render(<Wrapper />);
const user = userEvent.setup();
const button = screen.getByText("Open Modal");
expect(screen.queryByText("Modal Content")).not.toBeInTheDocument();
await user.click(button);
expect(screen.getByText("Modal Content")).toBeInTheDocument();
await user.keyboard("{Escape}");
expect(screen.queryByText("Modal Content")).not.toBeInTheDocument();
});
it("does not close on esc when using closeOnEsc=false", async () => {
const Wrapper = () => {
const modal = useModal();
return (
<CunninghamProvider>
<button onClick={modal.open}>Open Modal</button>
<Modal size={ModalSize.SMALL} closeOnEsc={false} {...modal}>
<div>Modal Content</div>
</Modal>
</CunninghamProvider>
);
};
render(<Wrapper />);
const user = userEvent.setup();
const button = screen.getByText("Open Modal");
expect(screen.queryByText("Modal Content")).not.toBeInTheDocument();
await user.click(button);
expect(screen.getByText("Modal Content")).toBeInTheDocument();
await user.keyboard("{Escape}");
expect(screen.queryByText("Modal Content")).toBeInTheDocument();
});
/** /**
* It should also prevent the modal from closing when pressing the escape key, but it appears * It should also prevent the modal from closing when pressing the escape key, but it appears

View File

@@ -80,6 +80,12 @@ export const CloseOnClickOutside: Story = {
closeOnClickOutside: true, closeOnClickOutside: true,
}, },
}; };
export const DontCloseOnEsc: Story = {
args: {
size: ModalSize.MEDIUM,
closeOnEsc: false,
},
};
export const PreventClose: Story = { export const PreventClose: Story = {
args: { args: {
size: ModalSize.MEDIUM, size: ModalSize.MEDIUM,

View File

@@ -51,6 +51,7 @@ export type ModalProps = PropsWithChildren & {
titleIcon?: React.ReactNode; titleIcon?: React.ReactNode;
hideCloseButton?: boolean; hideCloseButton?: boolean;
closeOnClickOutside?: boolean; closeOnClickOutside?: boolean;
closeOnEsc?: boolean;
preventClose?: boolean; preventClose?: boolean;
}; };
@@ -71,7 +72,7 @@ export const Modal = (props: ModalProps) => {
return <ModalInner {...props} />; return <ModalInner {...props} />;
}; };
export const ModalInner = (props: ModalProps) => { export const ModalInner = ({ closeOnEsc = true, ...props }: ModalProps) => {
const { modalParentSelector } = useModals(); const { modalParentSelector } = useModals();
if (!props.isOpen) { if (!props.isOpen) {
@@ -90,6 +91,7 @@ export const ModalInner = (props: ModalProps) => {
overlayClassName="c__modal__backdrop" overlayClassName="c__modal__backdrop"
className={classNames(MODAL_CLASS, `${MODAL_CLASS}--${props.size}`)} className={classNames(MODAL_CLASS, `${MODAL_CLASS}--${props.size}`)}
shouldCloseOnOverlayClick={!!props.closeOnClickOutside} shouldCloseOnOverlayClick={!!props.closeOnClickOutside}
shouldCloseOnEsc={closeOnEsc}
bodyOpenClassName={classNames("c__modals--opened", NOSCROLL_CLASS)} bodyOpenClassName={classNames("c__modals--opened", NOSCROLL_CLASS)}
> >
{!props.hideCloseButton && !props.preventClose && ( {!props.hideCloseButton && !props.preventClose && (