From d7892a6b148a7e8c99399886783821e91b58f382 Mon Sep 17 00:00:00 2001 From: KrotovM Date: Fri, 6 Mar 2026 17:56:29 +0200 Subject: [PATCH] feat(modal): separate close btn from dismissible --- apps/web/content/docs/components/modal.mdx | 2 +- .../ui/src/components/Modal/Modal.test.tsx | 44 ++++++++++++++++--- .../ui/src/components/Modal/ModalHeader.tsx | 3 +- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/apps/web/content/docs/components/modal.mdx b/apps/web/content/docs/components/modal.mdx index d0cec7fd08..22302b18d1 100644 --- a/apps/web/content/docs/components/modal.mdx +++ b/apps/web/content/docs/components/modal.mdx @@ -19,7 +19,7 @@ Use the `` React component to show a dialog element to the user with a he The visibility of the component can be set by passing a boolean value to the `show` prop on the `` component and we recommend you to do this via the React state. -Using a `openModal` and `setOpenModal` state for the main React component should allow you to easily manage the state of the Modal component and use inline functions on event listeners such as `onClick` or `onClose`. +Using a `openModal` and `setOpenModal` state for the main React component should allow you to easily manage the state of the Modal component and use inline functions on event listeners such as `onClick` or `onClose`. When you pass the `onClose` prop, the header close (X) button is shown and calls `onClose` when clicked. diff --git a/packages/ui/src/components/Modal/Modal.test.tsx b/packages/ui/src/components/Modal/Modal.test.tsx index aa522c9b74..ef6015b2b0 100644 --- a/packages/ui/src/components/Modal/Modal.test.tsx +++ b/packages/ui/src/components/Modal/Modal.test.tsx @@ -26,10 +26,23 @@ describe("Components / Modal", () => { expect(modal).not.toBeInTheDocument(); }); - it("should not render close button when modal is not dismissible", async () => { + it("should not close on backdrop press when dismissible but onClose is not provided", async () => { const user = userEvent.setup(); - render(); + render(); + + await user.click(triggerButton()); + const modal = dialog(); + expect(modal).toBeInTheDocument(); + + await user.click(dialogOverlay()); + expect(modal).toBeInTheDocument(); + }); + + it("should not render close button when onClose is not provided", async () => { + const user = userEvent.setup(); + + render(); await user.click(triggerButton()); @@ -40,10 +53,10 @@ describe("Components / Modal", () => { expect(closeButton).not.toBeInTheDocument(); }); - it("should render close button when modal is dismissible", async () => { + it("should render close button when onClose is provided", async () => { const user = userEvent.setup(); - render(); + render(); await user.click(triggerButton()); @@ -110,7 +123,7 @@ describe("Components / Modal", () => { it("should close `Modal` when `Space` is pressed on any of its buttons", async () => { const user = userEvent.setup(); - render(); + render(); const openButton = triggerButton(); @@ -145,7 +158,7 @@ describe("Components / Modal", () => { const user = userEvent.setup(); const inputRef = createRef(); - render(); + render(); await user.click(triggerButton()); const modal = dialog(); @@ -219,6 +232,25 @@ const TestModal = ({ ); }; +const TestModalWithoutOnClose = ({ + root, + dismissible = false, +}: Pick): JSX.Element => { + const [open, setOpen] = useState(false); + + return ( + <> + + + Terms of Service + +

Modal without onClose

+
+
+ + ); +}; + const dialog = () => screen.getByRole("dialog"); const dialogOverlay = () => screen.getByTestId("modal-overlay"); const triggerButton = () => screen.getByRole("button", { name: "Toggle modal" }); diff --git a/packages/ui/src/components/Modal/ModalHeader.tsx b/packages/ui/src/components/Modal/ModalHeader.tsx index fc15f03065..cd7dbba97c 100644 --- a/packages/ui/src/components/Modal/ModalHeader.tsx +++ b/packages/ui/src/components/Modal/ModalHeader.tsx @@ -31,7 +31,6 @@ export const ModalHeader = forwardRef((props, clearTheme: rootClearTheme, applyTheme: rootApplyTheme, popup, - dismissible, onClose, setHeaderId, } = useModalContext(); @@ -65,7 +64,7 @@ export const ModalHeader = forwardRef((props, {children} - {dismissible && ( + {onClose != null && (