From ca70a7cfa7d7860e7e4f89b9d2f51d2b5aa78edb Mon Sep 17 00:00:00 2001 From: cliffhall Date: Mon, 10 Aug 2026 23:46:32 -0400 Subject: [PATCH] chore(storybook): drop the redundant setProjectAnnotations setup file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since Storybook 10.3, @storybook/addon-vitest provisions the preview annotations itself — and *skips* doing so when it finds a setup file calling setProjectAnnotations. So .storybook/vitest.setup.ts was not merely redundant, it was actively opting the project out of the automatic path, which the addon printed a notice about on every run. Remove the file and the `setupFiles` entry from the `storybook` vitest project. A green suite doesn't prove the automatic provisioning works: without the preview annotations, stories would render outside MantineProvider and without App.css, and would very likely still pass. Add src/test/PreviewAnnotations.stories.tsx, which asserts from a play function that the Mantine theme variables and the App.css tokens are actually present in the rendered document, so an unthemed render fails loudly. Verified by emptying `decorators` in preview.tsx and confirming the story goes red. The a11y addon's axe runner can't be introspected from a play function, so it was checked out-of-band: a story with a real violation turned the suite red with the setup file already removed, confirming @storybook/addon-a11y/preview is applied automatically. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_0119LaPQ3v4NyBK3ZTdM9j84 --- clients/web/.storybook/vitest.setup.ts | 7 -- .../src/test/PreviewAnnotations.stories.tsx | 78 +++++++++++++++++++ clients/web/vite.config.ts | 10 ++- 3 files changed, 87 insertions(+), 8 deletions(-) delete mode 100644 clients/web/.storybook/vitest.setup.ts create mode 100644 clients/web/src/test/PreviewAnnotations.stories.tsx diff --git a/clients/web/.storybook/vitest.setup.ts b/clients/web/.storybook/vitest.setup.ts deleted file mode 100644 index fd7ac45e..00000000 --- a/clients/web/.storybook/vitest.setup.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview"; -import { setProjectAnnotations } from "@storybook/react-vite"; -import * as projectAnnotations from "./preview"; - -// This is an important step to apply the right configuration when testing your stories. -// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations -setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]); diff --git a/clients/web/src/test/PreviewAnnotations.stories.tsx b/clients/web/src/test/PreviewAnnotations.stories.tsx new file mode 100644 index 00000000..50cafe5d --- /dev/null +++ b/clients/web/src/test/PreviewAnnotations.stories.tsx @@ -0,0 +1,78 @@ +import { Button } from "@mantine/core"; +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { expect, within } from "storybook/test"; +import { theme } from "../theme/theme"; + +// Guard for #1898. +// +// `.storybook/vitest.setup.ts` used to call `setProjectAnnotations([...])` to +// hand the preview annotations to the Storybook vitest project. Since Storybook +// 10.3 `@storybook/addon-vitest` applies them automatically — and *skips* doing +// so when it finds such a setup file — so the file was removed and the +// `setupFiles` entry dropped from the `storybook` project in `vite.config.ts`. +// +// A green suite is not evidence that the automatic path works. Two things the +// setup file used to provision are silently losable: +// +// • `./preview`'s decorator, which wraps every story in `MantineProvider` +// with the project theme, and its `import "../src/App.css"`, which defines +// the `--inspector-*` tokens. Without them stories render unthemed and +// unstyled — and would very likely still pass their play functions. +// • `@storybook/addon-a11y/preview`, which runs axe after each play function. +// Without it the `a11y: { test: "error" }` parameter is inert and every +// story passes its accessibility check vacuously. +// +// The story below closes the first gap: it asserts the Mantine theme variables +// and the `App.css` tokens are actually present in the rendered document, so an +// unthemed render fails loudly rather than passing. It also asserts the preview +// `parameters` reached the story, which is the same channel the a11y parameter +// arrives on. The axe *runner* itself can't be introspected from a play +// function; it was verified out-of-band by temporarily giving a story a real +// violation and confirming the suite went red (see the PR for #1898). + +// The primary shade the theme pins for the light scheme — the value Mantine +// derives `--mantine-primary-color-filled` from. Read from the theme rather +// than hard-coded so a palette change can't silently invalidate the guard. +const LIGHT_PRIMARY_SHADE = 7; + +function expectedPrimaryColor(): string { + const palette = theme.colors?.[theme.primaryColor ?? ""]; + const color = palette?.[LIGHT_PRIMARY_SHADE]; + if (!color) throw new Error("theme is missing its primary color palette"); + return color; +} + +const meta: Meta = { + title: "Meta/Preview Annotations", + component: Button, + args: { children: "Themed" }, +}; + +export default meta; +type Story = StoryObj; + +export const ProjectAnnotationsApplied: Story = { + play: async ({ canvasElement, parameters }) => { + const canvas = within(canvasElement); + expect(canvas.getByRole("button", { name: "Themed" })).toBeInTheDocument(); + + const root = getComputedStyle(document.documentElement); + + // `MantineProvider` (the `./preview` decorator) injects the theme's CSS + // variables onto `:root`. No provider, no variables. + expect(root.getPropertyValue("--mantine-primary-color-filled").trim()).toBe( + expectedPrimaryColor(), + ); + + // `--inspector-brand-primary` is defined in `App.css`, which only reaches + // the story through `./preview`'s stylesheet import — and it resolves + // *through* the Mantine variable above, so this covers both layers. + expect(root.getPropertyValue("--inspector-brand-primary").trim()).toBe( + expectedPrimaryColor(), + ); + + // The preview `parameters` merged into the story context — the same channel + // that carries `a11y: { test: "error" }` to the a11y addon. + expect(parameters.a11y).toMatchObject({ test: "error" }); + }, +}; diff --git a/clients/web/vite.config.ts b/clients/web/vite.config.ts index 3b99d672..a31c0169 100644 --- a/clients/web/vite.config.ts +++ b/clients/web/vite.config.ts @@ -379,7 +379,15 @@ export default defineConfig(({ command }) => { }, ], }, - setupFiles: [".storybook/vitest.setup.ts"], + // No `setupFiles`: since Storybook 10.3 `@storybook/addon-vitest` + // provisions the preview annotations (`.storybook/preview.tsx` plus + // `@storybook/addon-a11y/preview`) itself, and *skips* doing so when + // it finds a setup file calling `setProjectAnnotations` — so the old + // `.storybook/vitest.setup.ts` was both redundant and actively + // opting out of the automatic path (#1898). A green suite doesn't + // prove the automatic provisioning works (stories rendered without + // the Mantine decorator would very likely still pass), so + // `src/test/PreviewAnnotations.stories.tsx` asserts it directly. }, }, ],