Storybook (apps/studio) — GOAL MODE
Canonical Octonode repository documentation.
Storybook (apps/studio) — GOAL MODE
Every component has a story. No exceptions. If you create a component, you create its
story in the same change. If you edit a component and it has no story yet, add one. A PR that
adds or changes a Studio component without its *.stories.tsx is incomplete.
Setup lives in apps/studio/.storybook/ (main.ts globs src/**/*.stories.@(ts|tsx);
preview.tsx wraps every story in the app's real providers — dark theme, React Query,
TooltipProvider). Run it: yarn workspace @octonode/studio storybook (dev on :6006) or
yarn workspace @octonode/studio build-storybook (static build → storybook-static/,
which emits the index.json the storybook MCP reads).
Where the story lives. Next to its component, named after it — never in the barrel:
ComponentName/
ComponentName.tsx
ComponentName.types.ts
ComponentName.stories.tsx # ← required; do NOT export it from index.ts
index.tsThe shape of a story (CSF3). Match the ui/ reference stories
(apps/studio/src/components/ui/button.stories.tsx is the canonical example):
import type { Meta, StoryObj } from "@storybook/react-vite";
import { ComponentName } from "./ComponentName";
const meta = {
title: "Studio/<FolderPath>/ComponentName", // "UI/<Name>" for ui/ atoms
component: ComponentName,
tags: ["autodocs"],
args: { /* realistic props built from ComponentName.types.ts */ },
} satisfies Meta<typeof ComponentName>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
// one extra story per meaningful state the props support (variants, status, empty/loading)Rules (do not violate):
preview.tsxalready supplies the dark theme,QueryClientProvider, andTooltipProvider. Don't re-wrap stories in those — add a story-localdecoratorsarray only for context a component genuinely needs (e.g.<ReactFlowProvider>for any@xyflow/reactnode/edge, a sized container for canvas components).- Build
argsfrom the component's real.types.ts. Never invent prop names. - Cover the states that matter — variants for atoms, status (ok/running/error/skipped) for run/execution views, populated vs empty for lists. One story per state, kept minimal.
- A page-level component that self-fetches via hooks renders empty without a running server;
that's acceptable — note it with a
// ponytail:comment rather than mocking the whole API. - Stories never import from a barrel's siblings by deep path and never modify the component.
Storybook MCP. .mcp.json (repo root) registers the storybook-mcp server pointed at
http://localhost:6006/index.json. Start Storybook first (yarn workspace @octonode/studio storybook), then the MCP can list components, read story args/props, and screenshot stories.
Visual regression
GitHub Actions runs yarn test:visual in the pinned Linux Playwright container and compares stories
whose transitive imports changed from the PR base commit (or the previous commit on pushes).
Changes to the shared Storybook preview or build configuration select every story; releases run the
full catalog. The check fails when any selected screenshot changes, a new story lacks a baseline, or
a removed story leaves a stale baseline.
Normal CI treats this job as advisory and writes a hosted review link to the Actions job summary. Open that link, select intended changes, choose Use latest, and select Update selected baselines. Approval creates one baseline commit on the source branch; the triggered CI rerun is the authority that decides whether the new baselines pass. Release candidates keep the strict, full-catalog gate.
If the hosted service is unavailable, download and extract the visual-review artifact, then run
VISUAL_REVIEW_DIR=/path/to/visual yarn test:visual:review. Select the intended changes, choose
Use latest, and select Update selected baselines. Leave Keep baseline selected when the UI
should be fixed instead. The artifact records Linux as its platform, so approval updates the canonical
files under tests/visual/baselines/linux/ on macOS without running Storybook, Playwright, or Docker.
Commit the locally approved baselines and push again. The review client is a React/Vite app in
apps/visual-review.
For CI-equivalent debugging on Linux, yarn test:visual compares affected stories against main
(VISUAL_BASE=<branch> overrides it), yarn test:visual:full runs the full catalog, and
VISUAL_STORY="Studio/Flow/Node/Default" yarn test:visual checks one story. Screenshot comparison is
skipped on other platforms so they cannot accidentally replace the Linux baselines.
Every checked story writes its current capture to test-results/visual/latest before comparison.
Failures emit one-line VISUAL_MISMATCH, VISUAL_MISSING_BASELINE, VISUAL_RENDER_ERROR, or
VISUAL_STALE_BASELINE JSON logs. Agents should use the named baseline/latest/diff files,
changed-pixel count, percentage, and changedBounds coordinates to inspect and fix the exact
affected region. Never approve a new baseline merely to make the gate green.
yarn test:visual:update replaces every Linux baseline and removes stale ones, so reserve it for an
explicitly approved bulk redesign in the pinned container. CI uploads the self-contained
visual-review artifact on every run.