diff --git a/site/.storybook/vitest.setup.ts b/site/.storybook/vitest.setup.ts index b2d3a795c3f..f11a4e41f52 100644 --- a/site/.storybook/vitest.setup.ts +++ b/site/.storybook/vitest.setup.ts @@ -1,7 +1,15 @@ import { setProjectAnnotations } from "@storybook/react-vite"; -import { beforeAll } from "vitest"; +import { beforeAll, beforeEach } from "vitest"; import * as previewAnnotations from "./preview"; const annotations = setProjectAnnotations([previewAnnotations]); beforeAll(annotations.beforeAll); + +// Radix DismissableLayer sets document.body.style.pointerEvents = "none" while +// a modal layer is active. When a story unmounts, the useEffect cleanup that +// restores body.pointerEvents can race with the next story's play function, +// causing false "pointer-events: none" failures on the first click. +beforeEach(() => { + document.body.style.pointerEvents = ""; +}); diff --git a/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx b/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx index 06091dac4c5..2a40a5127e2 100644 --- a/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx +++ b/site/src/modules/dashboard/Navbar/MobileMenu.stories.tsx @@ -1,5 +1,4 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { PointerEventsCheckLevel } from "@testing-library/user-event"; import type { FC } from "react"; import { fn, userEvent, within } from "storybook/test"; import { @@ -96,7 +95,7 @@ export const Member: Story = { export const ProxySettings: Story = { play: async ({ canvasElement }) => { - const user = setupUser(); + const user = userEvent.setup(); const body = within(canvasElement.ownerDocument.body); const menuItem = await body.findByRole("menuitem", { name: /workspace proxy settings/i, @@ -107,7 +106,7 @@ export const ProxySettings: Story = { export const UserSettings: Story = { play: async ({ canvasElement }) => { - const user = setupUser(); + const user = userEvent.setup(); const body = within(canvasElement.ownerDocument.body); const menuItem = await body.findByRole("menuitem", { name: /user settings/i, @@ -124,21 +123,12 @@ function withNavbarMock(Story: FC) { ); } -function setupUser() { - // It seems the dropdown component is disabling pointer events, which is - // causing Testing Library to throw an error. As a workaround, we can - // disable the pointer events check. - return userEvent.setup({ - pointerEventsCheck: PointerEventsCheckLevel.Never, - }); -} - async function openAdminSettings({ canvasElement, }: { canvasElement: HTMLElement; }) { - const user = setupUser(); + const user = userEvent.setup(); const body = within(canvasElement.ownerDocument.body); const menuItem = await body.findByRole("menuitem", { name: /admin settings/i, diff --git a/site/src/pages/WorkspacesPage/BatchUpdateModalForm.stories.tsx b/site/src/pages/WorkspacesPage/BatchUpdateModalForm.stories.tsx index 02835883cab..779f5524320 100644 --- a/site/src/pages/WorkspacesPage/BatchUpdateModalForm.stories.tsx +++ b/site/src/pages/WorkspacesPage/BatchUpdateModalForm.stories.tsx @@ -210,25 +210,7 @@ export const RunningWorkspacesFailedValidation: Story = { ); const updateButton = modal.getByRole("button", { name: "Update" }); - await userEvent.click(updateButton, { - /** - * @todo 2025-07-15 - Something in the test setup is causing the - * Update button to get treated as though it should opt out of - * pointer events, which causes userEvent to break. All of our code - * seems to be fine - we do have logic to disable pointer events, - * but only when the button is obviously configured wrong (e.g., - * it's configured as a link but has no URL). - * - * Disabling this check makes things work again, but shoots our - * confidence for how accessible the UI is, even if we know that at - * this point, the button exists, has the right text content, and is - * not disabled. - * - * We should aim to remove this property as soon as possible, - * opening up an issue upstream if necessary. - */ - pointerEventsCheck: 0, - }); + await userEvent.click(updateButton); await modal.findByText("Please acknowledge risks to continue."); const checkbox = modal.getByRole("checkbox", {