From 026d696a67c3958266611b0d07d02f8f072f883e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McKayla=20=E3=81=AF=E3=81=AA?= Date: Mon, 29 Jun 2026 19:42:19 +0000 Subject: [PATCH 01/10] chore(site): replace isChromatic with isPixel Use @coder/pixel-storybook's isPixel() for snapshot-determinism gating in place of chromatic/isChromatic. Bump pixel-storybook to the canary that exports isPixel and drop the now-unused chromatic dependency. --- site/.knip.jsonc | 1 - site/.storybook/preview.tsx | 4 +-- site/package.json | 3 +- site/pnpm-lock.yaml | 29 +++++-------------- site/src/components/Spinner/Spinner.tsx | 4 +-- .../workspaces/generateWorkspaceName.ts | 4 +-- .../pages/CliInstallPage/CliInstallPage.tsx | 4 +-- site/src/pages/TaskPage/TaskPage.tsx | 4 +-- 8 files changed, 18 insertions(+), 35 deletions(-) diff --git a/site/.knip.jsonc b/site/.knip.jsonc index c4d0b3152c5b7..d12174a6f96e6 100644 --- a/site/.knip.jsonc +++ b/site/.knip.jsonc @@ -23,7 +23,6 @@ "ignoreBinaries": ["protoc"], "ignoreDependencies": [ "@babel/plugin-syntax-typescript", - "@coder/pixel-storybook", "@types/react-virtualized-auto-sizer", "ts-proto" ] diff --git a/site/.storybook/preview.tsx b/site/.storybook/preview.tsx index ebfc1b383cd28..b9b56c0f4a2f2 100644 --- a/site/.storybook/preview.tsx +++ b/site/.storybook/preview.tsx @@ -1,5 +1,6 @@ import "../src/index.css"; import "../src/theme/globalFonts"; +import { isPixel } from "@coder/pixel-storybook"; import { ThemeProvider as EmotionThemeProvider } from "@emotion/react"; import CssBaseline from "@mui/material/CssBaseline"; import { @@ -8,7 +9,6 @@ import { } from "@mui/material/styles"; import { DecoratorHelpers } from "@storybook/addon-themes"; import type { Decorator, Loader, Parameters } from "@storybook/react-vite"; -import isChromatic from "chromatic/isChromatic"; import { StrictMode } from "react"; import { QueryClient, QueryClientProvider } from "react-query"; import { withRouter } from "storybook-addon-remix-react-router"; @@ -135,4 +135,4 @@ const fontLoader = async () => ({ }); export const loaders: Loader[] = - isChromatic() && document.fonts ? [fontLoader] : []; + isPixel() && document.fonts ? [fontLoader] : []; diff --git a/site/package.json b/site/package.json index 9db41828d382c..60bec81250f88 100644 --- a/site/package.json +++ b/site/package.json @@ -125,7 +125,7 @@ "@babel/plugin-syntax-typescript": "7.29.7", "@biomejs/biome": "2.4.10", "@chromatic-com/storybook": "5.0.1", - "@coder/pixel-storybook": "0.1.3", + "@coder/pixel-storybook": "0.1.3-canary.20260629.d99bfaa", "@octokit/types": "12.6.0", "@playwright/test": "1.50.1", "@rolldown/plugin-babel": "0.2.3", @@ -162,7 +162,6 @@ "@vitest/browser-playwright": "4.1.7", "autoprefixer": "10.5.0", "babel-plugin-react-compiler": "1.0.0", - "chromatic": "11.29.0", "dpdm": "3.15.1", "express": "4.21.2", "jest-canvas-mock": "2.5.2", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 241100290e101..eb9fff6862501 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -292,8 +292,8 @@ importers: specifier: 5.0.1 version: 5.0.1(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@coder/pixel-storybook': - specifier: 0.1.3 - version: 0.1.3(playwright-core@1.55.1) + specifier: 0.1.3-canary.20260629.d99bfaa + version: 0.1.3-canary.20260629.d99bfaa(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@octokit/types': specifier: 12.6.0 version: 12.6.0 @@ -402,9 +402,6 @@ importers: babel-plugin-react-compiler: specifier: 1.0.0 version: 1.0.0 - chromatic: - specifier: 11.29.0 - version: 11.29.0 dpdm: specifier: 3.15.1 version: 3.15.1 @@ -691,12 +688,13 @@ packages: peerDependencies: storybook: ^0.0.0-0 || ^10.1.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 - '@coder/pixel-storybook@0.1.3': - resolution: {integrity: sha512-vNBha7mU1PDFwauWcw2JV0hni980qu71meBOuhneaGbL4BL+06L4PEG6wYElhqJcs6pVPaUypj+bF+089ohZyw==, tarball: https://registry.npmjs.org/@coder/pixel-storybook/-/pixel-storybook-0.1.3.tgz} + '@coder/pixel-storybook@0.1.3-canary.20260629.d99bfaa': + resolution: {integrity: sha512-wLRsYiP95eH5DRIvzGLXhhW74+soBO0wHKsXi9bW2miol2/BtSmc1mNv5MY4d4t7IAr25u7/tVawFKhW2WUE8w==, tarball: https://registry.npmjs.org/@coder/pixel-storybook/-/pixel-storybook-0.1.3-canary.20260629.d99bfaa.tgz} engines: {node: '>=22'} hasBin: true peerDependencies: playwright-core: '>=1.47.2' + storybook: '>=8.0.0' '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==, tarball: https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz} @@ -3326,18 +3324,6 @@ packages: chroma-js@2.6.0: resolution: {integrity: sha512-BLHvCB9s8Z1EV4ethr6xnkl/P2YRFOGqfgvuMG/MyCbZPrTA+NeiByY6XvgF0zP4/2deU2CXnWyMa3zu1LqQ3A==, tarball: https://registry.npmjs.org/chroma-js/-/chroma-js-2.6.0.tgz} - chromatic@11.29.0: - resolution: {integrity: sha512-yisBlntp9hHVj19lIQdpTlcYIXuU9H/DbFuu6tyWHmj6hWT2EtukCCcxYXL78XdQt1vm2GfIrtgtKpj/Rzmo4A==, tarball: https://registry.npmjs.org/chromatic/-/chromatic-11.29.0.tgz} - hasBin: true - peerDependencies: - '@chromatic-com/cypress': ^0.*.* || ^1.0.0 - '@chromatic-com/playwright': ^0.*.* || ^1.0.0 - peerDependenciesMeta: - '@chromatic-com/cypress': - optional: true - '@chromatic-com/playwright': - optional: true - chromatic@13.3.4: resolution: {integrity: sha512-TR5rvyH0ESXobBB3bV8jc87AEAFQC7/n+Eb4XWhJz6hW3YNxIQPVjcbgLv+a4oKHEl1dUBueWSoIQsOVGTd+RQ==, tarball: https://registry.npmjs.org/chromatic/-/chromatic-13.3.4.tgz} hasBin: true @@ -6832,7 +6818,7 @@ snapshots: - '@chromatic-com/cypress' - '@chromatic-com/playwright' - '@coder/pixel-storybook@0.1.3(playwright-core@1.55.1)': + '@coder/pixel-storybook@0.1.3-canary.20260629.d99bfaa(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: get-port-please: 3.1.2 odiff-bin: 2.6.1 @@ -6840,6 +6826,7 @@ snapshots: playwright-core: 1.55.1 pngjs: 7.0.0 serve-handler: 6.1.7 + storybook: 10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) tsx: 4.22.4 zod: 3.23.8 @@ -9570,8 +9557,6 @@ snapshots: chroma-js@2.6.0: {} - chromatic@11.29.0: {} - chromatic@13.3.4: {} class-variance-authority@0.7.1: diff --git a/site/src/components/Spinner/Spinner.tsx b/site/src/components/Spinner/Spinner.tsx index b8abc7f9e57e8..a0db6a9749cd1 100644 --- a/site/src/components/Spinner/Spinner.tsx +++ b/site/src/components/Spinner/Spinner.tsx @@ -4,7 +4,7 @@ * https://v0.dev/ help. */ -import isChromatic from "chromatic/isChromatic"; +import { isPixel } from "@coder/pixel-storybook"; import { cva, type VariantProps } from "class-variance-authority"; import type { ReactNode } from "react"; import { cn } from "#/utils/cn"; @@ -59,7 +59,7 @@ export function Spinner({ rx="1" // 0.8 = leaves * 0.1 className={ - isChromatic() ? "" : "animate-[loading_0.8s_ease-in-out_infinite]" + isPixel() ? "" : "animate-[loading_0.8s_ease-in-out_infinite]" } style={{ transform: `rotate(${leaf * (360 / leaves.length)}deg)`, diff --git a/site/src/modules/workspaces/generateWorkspaceName.ts b/site/src/modules/workspaces/generateWorkspaceName.ts index 9dff54a59b4f5..adfa8d0e31a24 100644 --- a/site/src/modules/workspaces/generateWorkspaceName.ts +++ b/site/src/modules/workspaces/generateWorkspaceName.ts @@ -1,4 +1,4 @@ -import isChromatic from "chromatic/isChromatic"; +import { isPixel } from "@coder/pixel-storybook"; import { animals, colors, @@ -7,7 +7,7 @@ import { } from "unique-names-generator"; export const generateWorkspaceName = () => { - if (isChromatic()) { + if (isPixel()) { return "yellow-bird-23"; } const numberDictionary = NumberDictionary.generate({ min: 0, max: 99 }); diff --git a/site/src/pages/CliInstallPage/CliInstallPage.tsx b/site/src/pages/CliInstallPage/CliInstallPage.tsx index b48fb8b7539b1..bad09be882d09 100644 --- a/site/src/pages/CliInstallPage/CliInstallPage.tsx +++ b/site/src/pages/CliInstallPage/CliInstallPage.tsx @@ -1,10 +1,10 @@ -import isChromatic from "chromatic/isChromatic"; +import { isPixel } from "@coder/pixel-storybook"; import type { FC } from "react"; import { pageTitle } from "#/utils/page"; import { CliInstallPageView } from "./CliInstallPageView"; const CliInstallPage: FC = () => { - const origin = isChromatic() ? "https://example.com" : location.origin; + const origin = isPixel() ? "https://example.com" : location.origin; return ( <> diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index d6bc91a4c6385..260cac016ab42 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -1,4 +1,4 @@ -import isChromatic from "chromatic/isChromatic"; +import { isPixel } from "@coder/pixel-storybook"; import { ArrowLeftIcon, PauseIcon, @@ -487,7 +487,7 @@ const TaskLogPreview: FC = ({ }) => { // Scroll to the bottom on mount since snapshot logs are static. const scrollToBottom = useCallback((el: HTMLDivElement | null) => { - if (!isChromatic() && el) { + if (!isPixel() && el) { el.scrollIntoView({ block: "end" }); } }, []); From 5b381cd0e6db2dde380151874bf7d211475a1ef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McKayla=20=E3=81=AF=E3=81=AA?= Date: Mon, 29 Jun 2026 19:49:37 +0000 Subject: [PATCH 02/10] chore(site): remove the Storybook font loader pixel-storybook waits for fonts before snapshotting, so the Chromatic-era font-loading workaround in the Storybook preview is redundant. --- site/.storybook/preview.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/site/.storybook/preview.tsx b/site/.storybook/preview.tsx index b9b56c0f4a2f2..f13f7e1f3c30e 100644 --- a/site/.storybook/preview.tsx +++ b/site/.storybook/preview.tsx @@ -1,6 +1,5 @@ import "../src/index.css"; import "../src/theme/globalFonts"; -import { isPixel } from "@coder/pixel-storybook"; import { ThemeProvider as EmotionThemeProvider } from "@emotion/react"; import CssBaseline from "@mui/material/CssBaseline"; import { @@ -8,7 +7,7 @@ import { StyledEngineProvider, } from "@mui/material/styles"; import { DecoratorHelpers } from "@storybook/addon-themes"; -import type { Decorator, Loader, Parameters } from "@storybook/react-vite"; +import type { Decorator, Parameters } from "@storybook/react-vite"; import { StrictMode } from "react"; import { QueryClient, QueryClientProvider } from "react-query"; import { withRouter } from "storybook-addon-remix-react-router"; @@ -127,12 +126,3 @@ const withTheme: Decorator = (Story, context) => { }; export const decorators: Decorator[] = [withRouter, withQuery, withTheme]; - -// Try to fix storybook rendering fonts inconsistently -// https://www.chromatic.com/docs/font-loading/#solution-c-check-fonts-have-loaded-in-a-loader -const fontLoader = async () => ({ - fonts: await document.fonts.ready, -}); - -export const loaders: Loader[] = - isPixel() && document.fonts ? [fontLoader] : []; From 2af08bb8cbbcd81bccec991d91873bbc49c31db9 Mon Sep 17 00:00:00 2001 From: McKayla Washburn-Love Date: Wed, 29 Jul 2026 19:17:24 +0000 Subject: [PATCH 03/10] chore: upgrade to @coder/pixel-storybook 0.3 --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index 3c813ff3ebd1e..d82bc86817f6b 100644 --- a/site/package.json +++ b/site/package.json @@ -124,7 +124,7 @@ "@babel/core": "7.29.7", "@babel/plugin-syntax-typescript": "7.29.7", "@biomejs/biome": "2.4.10", - "@coder/pixel-storybook": "0.2.2", + "@coder/pixel-storybook": "0.3.0", "@octokit/types": "12.6.0", "@playwright/test": "1.50.1", "@rolldown/plugin-babel": "0.2.3", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 3cc1892add9dd..d8c14abd19ae3 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -289,8 +289,8 @@ importers: specifier: 2.4.10 version: 2.4.10 '@coder/pixel-storybook': - specifier: 0.2.2 - version: 0.2.2(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + specifier: 0.3.0 + version: 0.3.0(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@octokit/types': specifier: 12.6.0 version: 12.6.0 @@ -682,8 +682,8 @@ packages: '@chevrotain/utils@11.1.2': resolution: {integrity: sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==, tarball: https://registry.npmjs.org/@chevrotain/utils/-/utils-11.1.2.tgz} - '@coder/pixel-storybook@0.2.2': - resolution: {integrity: sha512-f1dJ7FiNCdoT9VRVH6IWFnCG3VMnavtvTx/r6sZ4hzhj/rJEZv4P69J2x4XdYaMAwi+O5xbp2UQf0dNf2U9lLA==, tarball: https://registry.npmjs.org/@coder/pixel-storybook/-/pixel-storybook-0.2.2.tgz} + '@coder/pixel-storybook@0.3.0': + resolution: {integrity: sha512-+M3tr9lfjN/B2v3XCYDeAcz4TqZnN80Xa1ezpBS1NejsmhGf6RePWDz9ksgLmHGJ0iCmAypjbCX8BFosOfYeoA==, tarball: https://registry.npmjs.org/@coder/pixel-storybook/-/pixel-storybook-0.3.0.tgz} engines: {node: '>=22'} hasBin: true peerDependencies: @@ -6779,7 +6779,7 @@ snapshots: '@chevrotain/utils@11.1.2': {} - '@coder/pixel-storybook@0.2.2(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': + '@coder/pixel-storybook@0.3.0(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: get-port-please: 3.1.2 jsonc-parser: 3.3.1 From 6bf07ddfa923efbb345c1797625dca8c547394ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McKayla=20=E3=81=AF=E3=81=AA?= Date: Thu, 30 Jul 2026 00:53:22 +0000 Subject: [PATCH 04/10] chore(site): import isPixel from the storyapi subpath pixel-storybook 0.3 moves isPixel to "@coder/pixel-storybook/storyapi". --- site/src/components/Spinner/Spinner.tsx | 2 +- site/src/modules/workspaces/generateWorkspaceName.ts | 2 +- site/src/pages/CliInstallPage/CliInstallPage.tsx | 2 +- site/src/pages/TaskPage/TaskPage.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/site/src/components/Spinner/Spinner.tsx b/site/src/components/Spinner/Spinner.tsx index a0db6a9749cd1..847aa26ab7065 100644 --- a/site/src/components/Spinner/Spinner.tsx +++ b/site/src/components/Spinner/Spinner.tsx @@ -4,7 +4,7 @@ * https://v0.dev/ help. */ -import { isPixel } from "@coder/pixel-storybook"; +import { isPixel } from "@coder/pixel-storybook/storyapi"; import { cva, type VariantProps } from "class-variance-authority"; import type { ReactNode } from "react"; import { cn } from "#/utils/cn"; diff --git a/site/src/modules/workspaces/generateWorkspaceName.ts b/site/src/modules/workspaces/generateWorkspaceName.ts index adfa8d0e31a24..5edf72f877de1 100644 --- a/site/src/modules/workspaces/generateWorkspaceName.ts +++ b/site/src/modules/workspaces/generateWorkspaceName.ts @@ -1,4 +1,4 @@ -import { isPixel } from "@coder/pixel-storybook"; +import { isPixel } from "@coder/pixel-storybook/storyapi"; import { animals, colors, diff --git a/site/src/pages/CliInstallPage/CliInstallPage.tsx b/site/src/pages/CliInstallPage/CliInstallPage.tsx index bad09be882d09..d406834ef532e 100644 --- a/site/src/pages/CliInstallPage/CliInstallPage.tsx +++ b/site/src/pages/CliInstallPage/CliInstallPage.tsx @@ -1,4 +1,4 @@ -import { isPixel } from "@coder/pixel-storybook"; +import { isPixel } from "@coder/pixel-storybook/storyapi"; import type { FC } from "react"; import { pageTitle } from "#/utils/page"; import { CliInstallPageView } from "./CliInstallPageView"; diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index 260cac016ab42..1c15ec4db2f36 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -1,4 +1,4 @@ -import { isPixel } from "@coder/pixel-storybook"; +import { isPixel } from "@coder/pixel-storybook/storyapi"; import { ArrowLeftIcon, PauseIcon, From bc8e035945c473358bc14cddc097cffdaebd1444 Mon Sep 17 00:00:00 2001 From: McKayla Washburn-Love Date: Wed, 29 Jul 2026 19:17:24 +0000 Subject: [PATCH 05/10] chore: upgrade to @coder/pixel-storybook 0.3 --- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/package.json b/site/package.json index 3c813ff3ebd1e..d82bc86817f6b 100644 --- a/site/package.json +++ b/site/package.json @@ -124,7 +124,7 @@ "@babel/core": "7.29.7", "@babel/plugin-syntax-typescript": "7.29.7", "@biomejs/biome": "2.4.10", - "@coder/pixel-storybook": "0.2.2", + "@coder/pixel-storybook": "0.3.0", "@octokit/types": "12.6.0", "@playwright/test": "1.50.1", "@rolldown/plugin-babel": "0.2.3", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 3cc1892add9dd..d8c14abd19ae3 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -289,8 +289,8 @@ importers: specifier: 2.4.10 version: 2.4.10 '@coder/pixel-storybook': - specifier: 0.2.2 - version: 0.2.2(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + specifier: 0.3.0 + version: 0.3.0(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@octokit/types': specifier: 12.6.0 version: 12.6.0 @@ -682,8 +682,8 @@ packages: '@chevrotain/utils@11.1.2': resolution: {integrity: sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==, tarball: https://registry.npmjs.org/@chevrotain/utils/-/utils-11.1.2.tgz} - '@coder/pixel-storybook@0.2.2': - resolution: {integrity: sha512-f1dJ7FiNCdoT9VRVH6IWFnCG3VMnavtvTx/r6sZ4hzhj/rJEZv4P69J2x4XdYaMAwi+O5xbp2UQf0dNf2U9lLA==, tarball: https://registry.npmjs.org/@coder/pixel-storybook/-/pixel-storybook-0.2.2.tgz} + '@coder/pixel-storybook@0.3.0': + resolution: {integrity: sha512-+M3tr9lfjN/B2v3XCYDeAcz4TqZnN80Xa1ezpBS1NejsmhGf6RePWDz9ksgLmHGJ0iCmAypjbCX8BFosOfYeoA==, tarball: https://registry.npmjs.org/@coder/pixel-storybook/-/pixel-storybook-0.3.0.tgz} engines: {node: '>=22'} hasBin: true peerDependencies: @@ -6779,7 +6779,7 @@ snapshots: '@chevrotain/utils@11.1.2': {} - '@coder/pixel-storybook@0.2.2(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': + '@coder/pixel-storybook@0.3.0(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: get-port-please: 3.1.2 jsonc-parser: 3.3.1 From a6f593d32cfff9d83d74390b104dcdccd68a3bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McKayla=20=E3=81=AF=E3=81=AA?= Date: Thu, 30 Jul 2026 02:00:41 +0000 Subject: [PATCH 06/10] test(site): fix failing storybook stories Six stories failed their play functions in the pixel snapshot run: - The four NavbarView admin stories click the admin settings dropdown, which is hidden below the `md` breakpoint. Pixel's tablet viewport is 744px wide, so those stories now snapshot desktop only. - CreateOAuth2AppPageView's Default story asserted the submit button was disabled before formik's validate-on-mount pass had reported the empty required fields. The assertion now retries. - WorkspaceSettingsPageView's update policy story asserted onSubmit was called with a single argument, but formik also passes its helper bag. --- .../modules/dashboard/Navbar/NavbarView.stories.tsx | 10 ++++++++++ .../CreateOAuth2AppPageView.stories.tsx | 12 ++++++++---- .../WorkspaceSettingsPageView.stories.tsx | 2 ++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx index 80c0986f3067f..6b851563093ef 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx @@ -15,6 +15,12 @@ const tasksFilter: TasksFilter = { owner: MockUserOwner.username, }; +// The admin settings dropdown is hidden below the `md` breakpoint, where the +// navbar collapses into `MobileMenu`, so stories that open the dropdown can +// only be snapshotted at desktop width. `MobileMenu.stories.tsx` covers the +// collapsed menu. +const desktopOnly = { pixel: { matrix: { viewports: ["desktop"] } } }; + const meta: Meta = { title: "modules/dashboard/NavbarView", parameters: { @@ -49,6 +55,7 @@ export default meta; type Story = StoryObj; export const ForAdmin: Story = { + parameters: desktopOnly, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click( @@ -58,6 +65,7 @@ export const ForAdmin: Story = { }; export const ForAuditor: Story = { + parameters: desktopOnly, args: { user: MockUserMember, adminPermissions: { @@ -73,6 +81,7 @@ export const ForAuditor: Story = { }; export const ForOrgAdmin: Story = { + parameters: desktopOnly, args: { user: MockUserMember, adminPermissions: { @@ -89,6 +98,7 @@ export const ForOrgAdmin: Story = { }; export const ForSingleOrgOSSAdmin: Story = { + parameters: desktopOnly, args: { adminPermissions: { canViewDeployment: true, diff --git a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx index 8197b2d1eaef2..72744a8ae4a9c 100644 --- a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { expect, spyOn, userEvent, within } from "storybook/test"; +import { expect, spyOn, userEvent, waitFor, within } from "storybook/test"; import { reactRouterParameters } from "storybook-addon-remix-react-router"; import { API } from "#/api/api"; import { @@ -41,9 +41,13 @@ export const Default: Story = { name: /add an oauth2 application/i, }), ).toBeVisible(); - await expect( - canvas.getByRole("button", { name: /create application/i }), - ).toBeDisabled(); + // The submit button renders enabled for a frame until the form's + // validate-on-mount pass reports the empty required fields. + await waitFor(() => + expect( + canvas.getByRole("button", { name: /create application/i }), + ).toBeDisabled(), + ); }, }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx index 7c2550c6e3940..f50f74b81f8d8 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSettingsPageView.stories.tsx @@ -46,6 +46,8 @@ export const UpdateAutomaticUpdatesPolicy: Story = { await waitFor(() => expect(args.onSubmit).toHaveBeenCalledWith( expect.objectContaining({ automatic_updates: "always" }), + // formik passes its helper bag as the second argument. + expect.anything(), ), ); }, From afcbf88e8d661c3def0fecebf0dd8540967ead75 Mon Sep 17 00:00:00 2001 From: McKayla Washburn-Love Date: Mon, 10 Aug 2026 19:21:17 +0000 Subject: [PATCH 07/10] idk --- site/.storybook/preview.tsx | 6 +++++- site/package.json | 2 +- site/pnpm-lock.yaml | 10 +++++----- .../modules/dashboard/Navbar/NavbarView.stories.tsx | 13 ++----------- .../CreateOAuth2AppPageView.stories.tsx | 10 ++++------ 5 files changed, 17 insertions(+), 24 deletions(-) diff --git a/site/.storybook/preview.tsx b/site/.storybook/preview.tsx index 5d554465f08a3..1e5ecd488b88a 100644 --- a/site/.storybook/preview.tsx +++ b/site/.storybook/preview.tsx @@ -7,7 +7,7 @@ import { StyledEngineProvider, } from "@mui/material/styles"; import { DecoratorHelpers } from "@storybook/addon-themes"; -import type { Decorator, Parameters } from "@storybook/react-vite"; +import type { Decorator, Parameters, Preview } from "@storybook/react-vite"; import { StrictMode } from "react"; import { QueryClient, QueryClientProvider } from "react-query"; import { withRouter } from "storybook-addon-remix-react-router"; @@ -131,3 +131,7 @@ const withTheme: Decorator = (Story, context) => { }; export const decorators: Decorator[] = [withRouter, withQuery, withTheme]; + +export const preview: Preview = { + initialGlobals: { matrix: {} }, +}; diff --git a/site/package.json b/site/package.json index aededc1127b53..3b98ab09f11ee 100644 --- a/site/package.json +++ b/site/package.json @@ -124,7 +124,7 @@ "@babel/core": "7.29.7", "@babel/plugin-syntax-typescript": "7.29.7", "@biomejs/biome": "2.4.10", - "@coder/pixel-storybook": "0.3.0", + "@coder/pixel-storybook": "0.3.0-canary.20260730.a903fd0", "@octokit/types": "12.6.0", "@playwright/test": "1.50.1", "@rolldown/plugin-babel": "0.2.3", diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml index 322a6a9205e6d..6244c8c2c062f 100644 --- a/site/pnpm-lock.yaml +++ b/site/pnpm-lock.yaml @@ -289,8 +289,8 @@ importers: specifier: 2.4.10 version: 2.4.10 '@coder/pixel-storybook': - specifier: 0.3.0 - version: 0.3.0(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + specifier: 0.3.0-canary.20260730.a903fd0 + version: 0.3.0-canary.20260730.a903fd0(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@octokit/types': specifier: 12.6.0 version: 12.6.0 @@ -679,8 +679,8 @@ packages: '@chevrotain/utils@11.1.2': resolution: {integrity: sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==, tarball: https://registry.npmjs.org/@chevrotain/utils/-/utils-11.1.2.tgz} - '@coder/pixel-storybook@0.3.0': - resolution: {integrity: sha512-+M3tr9lfjN/B2v3XCYDeAcz4TqZnN80Xa1ezpBS1NejsmhGf6RePWDz9ksgLmHGJ0iCmAypjbCX8BFosOfYeoA==, tarball: https://registry.npmjs.org/@coder/pixel-storybook/-/pixel-storybook-0.3.0.tgz} + '@coder/pixel-storybook@0.3.0-canary.20260730.a903fd0': + resolution: {integrity: sha512-mQ8xBj3z/P6fThmoZvj4w9DL/PS+i4BfazL2Zp40qCMuB1XwpCnH7hdrCMe93wJbxrOdoV5yfBwM7Bv5gy/7Xg==, tarball: https://registry.npmjs.org/@coder/pixel-storybook/-/pixel-storybook-0.3.0-canary.20260730.a903fd0.tgz} engines: {node: '>=22'} hasBin: true peerDependencies: @@ -6764,7 +6764,7 @@ snapshots: '@chevrotain/utils@11.1.2': {} - '@coder/pixel-storybook@0.3.0(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': + '@coder/pixel-storybook@0.3.0-canary.20260730.a903fd0(playwright-core@1.55.1)(storybook@10.3.3(@testing-library/dom@10.4.0)(prettier@3.4.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: get-port-please: 3.1.2 jsonc-parser: 3.3.1 diff --git a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx index 6b851563093ef..e7df5beea20ab 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.stories.tsx @@ -15,12 +15,6 @@ const tasksFilter: TasksFilter = { owner: MockUserOwner.username, }; -// The admin settings dropdown is hidden below the `md` breakpoint, where the -// navbar collapses into `MobileMenu`, so stories that open the dropdown can -// only be snapshotted at desktop width. `MobileMenu.stories.tsx` covers the -// collapsed menu. -const desktopOnly = { pixel: { matrix: { viewports: ["desktop"] } } }; - const meta: Meta = { title: "modules/dashboard/NavbarView", parameters: { @@ -55,7 +49,6 @@ export default meta; type Story = StoryObj; export const ForAdmin: Story = { - parameters: desktopOnly, play: async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.click( @@ -65,7 +58,6 @@ export const ForAdmin: Story = { }; export const ForAuditor: Story = { - parameters: desktopOnly, args: { user: MockUserMember, adminPermissions: { @@ -81,7 +73,6 @@ export const ForAuditor: Story = { }; export const ForOrgAdmin: Story = { - parameters: desktopOnly, args: { user: MockUserMember, adminPermissions: { @@ -89,7 +80,8 @@ export const ForOrgAdmin: Story = { canViewOrganizations: true, }, }, - play: async ({ canvasElement }) => { + play: async ({ canvasElement, globals }) => { + console.log(globals); const canvas = within(canvasElement); await userEvent.click( canvas.getByRole("button", { name: "Admin settings" }), @@ -98,7 +90,6 @@ export const ForOrgAdmin: Story = { }; export const ForSingleOrgOSSAdmin: Story = { - parameters: desktopOnly, args: { adminPermissions: { canViewDeployment: true, diff --git a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx index 72744a8ae4a9c..1015a460345bb 100644 --- a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/CreateOAuth2AppPageView.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { expect, spyOn, userEvent, waitFor, within } from "storybook/test"; +import { expect, spyOn, userEvent, within } from "storybook/test"; import { reactRouterParameters } from "storybook-addon-remix-react-router"; import { API } from "#/api/api"; import { @@ -43,11 +43,9 @@ export const Default: Story = { ).toBeVisible(); // The submit button renders enabled for a frame until the form's // validate-on-mount pass reports the empty required fields. - await waitFor(() => - expect( - canvas.getByRole("button", { name: /create application/i }), - ).toBeDisabled(), - ); + expect( + await canvas.findByRole("button", { name: /create application/i }), + ).toBeDisabled(); }, }; From 5a499ce3bf224bce0c169c94d5dfc586607d452e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?McKayla=20=E3=81=AF=E3=81=AA?= Date: Mon, 10 Aug 2026 20:29:49 +0000 Subject: [PATCH 08/10] test(site): fix the last two failing storybook stories - IconField's OpenPicker story looked for an emoji-mart category label with a testing-library text query. emoji-mart renders into a shadow root, so the label was never reachable; the assertion now reads the host element's shadow DOM, scoped to the popover so it cannot match the hidden picker the field keeps mounted to warm the lazy chunk. - OAuth2AppsSettingsPage's settings stories looked for the Enable button immediately after switching tabs, before the settings section had replaced its loader. Both now retry. --- site/src/components/IconField/IconField.stories.tsx | 13 +++++++++++-- .../OAuth2AppsSettingsPage.stories.tsx | 8 ++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/site/src/components/IconField/IconField.stories.tsx b/site/src/components/IconField/IconField.stories.tsx index f9412f259683f..131ba69cedfd3 100644 --- a/site/src/components/IconField/IconField.stories.tsx +++ b/site/src/components/IconField/IconField.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import { expect, fn, screen, userEvent, within } from "storybook/test"; +import { expect, fn, screen, userEvent, waitFor, within } from "storybook/test"; import { IconField } from "./IconField"; const meta: Meta = { @@ -50,6 +50,15 @@ export const OpenPicker: Story = { }); await userEvent.click(button); await expect(button).toHaveAttribute("aria-expanded", "true"); - await expect(await screen.findByText("Smileys & People")).toBeVisible(); + // emoji-mart renders the picker into a shadow root, which testing-library + // queries cannot reach, so the category list is read off the host element. + // The popover scopes the lookup to the picker the click opened, since the + // field also keeps a hidden copy mounted to warm the lazy chunk. + const popover = await screen.findByRole("dialog"); + await waitFor(() => + expect( + popover.querySelector("em-emoji-picker")?.shadowRoot?.textContent, + ).toContain("Smileys & People"), + ); }, }; diff --git a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage.stories.tsx b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage.stories.tsx index 4a2da8d58c60d..b29df894c07b6 100644 --- a/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage.stories.tsx +++ b/site/src/pages/DeploymentSettingsPage/OAuth2AppsSettingsPage/OAuth2AppsSettingsPage.stories.tsx @@ -47,7 +47,9 @@ export const CanEditSettings: Story = { const canvas = within(canvasElement); await userEvent.click(canvas.getByRole("tab", { name: "Settings" })); - await expect(canvas.getByRole("button", { name: "Enable" })).toBeEnabled(); + await expect( + await canvas.findByRole("button", { name: "Enable" }), + ).toBeEnabled(); }, }; @@ -68,7 +70,9 @@ export const ViewOnlyCannotEditSettings: Story = { const canvas = within(canvasElement); await userEvent.click(canvas.getByRole("tab", { name: "Settings" })); - await expect(canvas.getByRole("button", { name: "Enable" })).toBeDisabled(); + await expect( + await canvas.findByRole("button", { name: "Enable" }), + ).toBeDisabled(); await expect( canvas.getByText(/permission to edit deployment configuration/), ).toBeVisible(); From e89c234b5a576af9e8bee8b1d0b5f2320bf54b7d Mon Sep 17 00:00:00 2001 From: McKayla Washburn-Love Date: Mon, 10 Aug 2026 22:35:05 +0000 Subject: [PATCH 09/10] Update IconField.stories.tsx --- site/src/components/IconField/IconField.stories.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/site/src/components/IconField/IconField.stories.tsx b/site/src/components/IconField/IconField.stories.tsx index 131ba69cedfd3..4d062eb1268c7 100644 --- a/site/src/components/IconField/IconField.stories.tsx +++ b/site/src/components/IconField/IconField.stories.tsx @@ -50,15 +50,11 @@ export const OpenPicker: Story = { }); await userEvent.click(button); await expect(button).toHaveAttribute("aria-expanded", "true"); - // emoji-mart renders the picker into a shadow root, which testing-library - // queries cannot reach, so the category list is read off the host element. - // The popover scopes the lookup to the picker the click opened, since the - // field also keeps a hidden copy mounted to warm the lazy chunk. const popover = await screen.findByRole("dialog"); await waitFor(() => expect( - popover.querySelector("em-emoji-picker")?.shadowRoot?.textContent, - ).toContain("Smileys & People"), + popover.querySelector("em-emoji-picker"), + ).toBeInTheDocument(), ); }, }; From e0e379c5fd5760ee566921f1f2bd35c5ea78488c Mon Sep 17 00:00:00 2001 From: McKayla Washburn-Love Date: Mon, 10 Aug 2026 22:37:39 +0000 Subject: [PATCH 10/10] :^) --- site/src/components/IconField/IconField.stories.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/site/src/components/IconField/IconField.stories.tsx b/site/src/components/IconField/IconField.stories.tsx index 4d062eb1268c7..7def38e2fe3e6 100644 --- a/site/src/components/IconField/IconField.stories.tsx +++ b/site/src/components/IconField/IconField.stories.tsx @@ -52,9 +52,7 @@ export const OpenPicker: Story = { await expect(button).toHaveAttribute("aria-expanded", "true"); const popover = await screen.findByRole("dialog"); await waitFor(() => - expect( - popover.querySelector("em-emoji-picker"), - ).toBeInTheDocument(), + expect(popover.querySelector("em-emoji-picker")).toBeInTheDocument(), ); }, };