Skip to content

chore(web): migrate the Storybook family to 10.x (Closes #425) - #449

Merged
Shironex merged 4 commits into
mainfrom
chore/storybook-10-migration
Aug 1, 2026
Merged

Shironex merged 4 commits into
mainfrom
chore/storybook-10-migration

Conversation

@Shironex

@Shironex Shironex commented Aug 1, 2026 •

Copy link
Copy Markdown
Collaborator

Moves the whole Storybook family from 9.1.20 to 10.5.5 in one deliberate change, because Storybook refuses to run unless every one of its packages sits on the same major.

Why this could not go through Dependabot

storybook, @storybook/react-vite, @storybook/addon-vitest and @storybook/addon-a11y are one migration. The two open Dependabot PRs each moved half of it and were structurally unmergeable:

After this change bun pm ls --all shows exactly one core and no nested copies:

@storybook/addon-a11y@10.5.5
@storybook/addon-vitest@10.5.5
@storybook/builder-vite@10.5.5
@storybook/csf-plugin@10.5.5
@storybook/react-dom-shim@10.5.5
@storybook/react-vite@10.5.5
@storybook/react@10.5.5
storybook@10.5.5

bun install --frozen-lockfile is clean.

The play-function Canvas sites — root cause, and why the diff is empty there

The issue predicted a code rewrite at every canvas.<query> site. It turned out none was needed, and that is the actual finding, so here is the evidence rather than a claim.

Canvas is not a Storybook-9 vs Storybook-10 API difference. It is a declaration-merging type that only assembles when one copy of core is installed:

  • Storybook 9 — storybook/dist/csf/index.d.ts declares interface Canvas {} (empty), and storybook/dist/test/index.d.ts fills it in via declare module 'storybook/internal/csf' { interface Canvas extends BoundFunctions<typeof queries> {} }.
  • Storybook 10 — storybook/dist/csf/index.d.ts:1843 declares interface Canvas extends BoundFunctions<typeof queries> {} directly. No augmentation needed.

In #414's split install the augmentation landed on a different physical module than the one @storybook/react-vite@9 resolved StoryContext['canvas'] from, so Canvas stayed the empty interface and every property access reported TS2339 — which is why the log listed one line per query call and looked like an API removal. With the family consistent, Canvas carries the testing-library queries again and all 50 call sites typecheck and run unchanged.

The issue's list was truncated at 12 lines; swept for all of them. Every play-function site that destructures { canvas } from the play context (7 files, 50 query calls) was verified green — by typecheck and by executing the play functions:

File play-context canvas query calls
settings/ClaudeNotifyHook/ClaudeNotifyHook.stories.tsx 3 — getByRole ×2, getByText
terminal/TerminalGrid/TerminalGrid.stories.tsx 7 — getAllByRole ×2, getAllByText, getByLabelText, getByRole, getByText, queryByRole
terminal/TerminalGridPane/TerminalGridPane.stories.tsx 12 — getByRole ×5, getByText ×3, getByLabelText ×2, queryByRole ×2
terminal/TerminalPane/TerminalPane.stories.tsx 7 — getByText ×6, queryByText
terminal/TerminalReadonlyPane/TerminalReadonlyPane.stories.tsx 4 — getByRole ×2, getByLabelText ×2
terminal/TerminalTabs/TerminalTabs.stories.tsx 16 — getByRole ×12, getByText ×2, getByLabelText, queryByRole
terminal/TerminalView/TerminalView.stories.tsx 1 — getByRole

The last four files were not in the issue's truncated list — they would have been the second surprise if only the three named files had been checked.

The other ~350 canvas.* calls in the repo come from within(canvasElement) / portaledSurface() (i.e. storybook/test's within), never touched the Canvas interface, and were unaffected either way.

Peer dependencies

storybook@10 declares three peers, all optional (peerDependenciesMeta), so nothing is newly required:

  • @types/react ^16 || ^17 || ^18 || ^19 — satisfied by the existing @types/react@^19.2.0.
  • vite-plus ^0.1.15 || ^0.2.0 — optional and not installed; it is the Vite+ toolchain, not something this repo uses. No action.
  • prettier ^2 || ^3 — optional, not installed.

The widened vite range (^5 || ^6 || ^7 || ^8, on @storybook/react-vite and @storybook/builder-vite) still covers the pinned vite@7.3.5 — it is a widening, not a floor bump, so nothing forces a Vite major. @storybook/addon-vitest now peers @vitest/browser ^3 || ^4, @vitest/runner ^3 || ^4, vitest ^3 || ^4 (all optional); the repo's vitest@3.2.7 / @vitest/browser@3.2.7 stay in range, so this migration does not drag in the Vitest 4 major.

Config

storybook automigrate --dry-run proposed five migrations; four are additive opt-ins that are out of scope for a version migration (eslintPlugin — new lint dependency, addon-mcp — new addon, addon-a11y-addon-test — the a11y test param is already set to 'todo' in preview.ts, wrap-getAbsolutePath — a PnP/monorepo robustness rewrite the current bun-hoisted install does not need; build-storybook is green without it).

The fifth (addon-globals-api) was run and then reverted: it rewrites parameters.backgrounds.disable → disabled, but Storybook 10's own preview runtime destructures disable (storybook/dist/preview/runtime.js:30702) and its type declares disable?: boolean (storybook/dist/csf/index.d.ts:1417). Applying it would have silently dead-lettered the parameter.

Every existing import specifier (storybook/test, storybook/manager-api, storybook/theming/create, @storybook/react-vite) is unchanged in 10, so no import rewrites were needed.

.storybook/vitest.setup.ts keeps its setProjectAnnotations call even though addon-vitest 10.3+ prints an INFO nudge to remove it — a comment now records why: the nudge is only true for the storybook Vitest project (which has the plugin). The unit project shares the same setup file and has no Storybook plugin, so removing the call would strip the preview decorators from every composed-story unit test.

The Dependabot group commit — separately revertible

The last commit adds a storybook group to .github/dependabot.yml with patterns: ["storybook", "@storybook/*"] and no update-types restriction, so it captures majors.

This does not conflict with the #398 decision to ungroup majors. That rationale was "one migration per PR" — and the Storybook family is one migration whose packages must move in lockstep. Without the group, every future Storybook major arrives as N individually-unmergeable PRs and a human has to hand-merge them into one branch, which is exactly the work this PR is doing. The group is listed first because Dependabot assigns a dependency to the first group it matches, which keeps the family together for minors and patches too (they have the same lockstep requirement).

It is deliberately its own final commit so it can be dropped with a single revert if you disagree, without touching the migration.

Test plan

  • bun run lint (eslint + lint:meta) — green
  • bun run typecheck + bunx tsc -b apps/web — green (this is where TS2339 would surface)
  • bun run codegen:check — green
  • bun run test:node — 2047 pass / 2 skip
  • bun run test:plugin — 15 pass
  • bun run --filter @nightcore/web test:stories — 206 files / 914 tests pass; this is the gate that actually executes every play function
  • bun run test:web (storybook + unit browser projects) — 514 files / 2937 tests pass
  • bun run --filter @nightcore/web build-storybook — builds successfully
  • bun install --frozen-lockfile — clean
  • bun run audit — no new advisories

CI is green on the same tree, including vitest browser coverage (apps/web) — the exact job that red-lined on #414.

Closes #425. #413 and #414 should be closed when this lands.

Storybook refuses to run unless every one of its packages sits on the same
major, so `storybook`, `@storybook/react-vite`, `@storybook/addon-vitest` and
`@storybook/addon-a11y` all move together in one change. Splitting them is what
made the two Dependabot PRs structurally unmergeable: #413 moved the framework
and died on `No matching export ... "Tag"` against core 9, #414 moved core only
and made bun resolve a nested storybook@9 under every `@storybook/*` package.

`bun pm ls --all` now shows exactly one core (storybook@10.5.5) and no nested
copies, and `bun install --frozen-lockfile` is clean.
… 10 bump

addon-vitest 10.3+ prints an INFO box telling you to delete this call because it
now provisions preview annotations itself. That is true only for the `storybook`
Vitest project (the one with the plugin). The `unit` project shares this setup
file and has no Storybook plugin, so following the nudge would strip the preview
decorators from every composed-story unit test.
@Shironex Shironex added dependencies Pull requests that update a dependency file area: web React board (apps/web) labels Aug 1, 2026
@github-project-automation github-project-automation Bot moved this to Todo in Nightcore Aug 1, 2026
@storybook/react-dom-shim ships dist/react-18.js in Storybook 10 (it was
dist/react-18.mjs in 9). Both optimizeDeps guard comments named the old file;
the guard itself is unchanged (it pre-bundles by package name), only the
explanation of WHICH module it covers was stale.
Storybook only runs when every one of its packages sits on the same major, so a
per-package major PR is structurally unmergeable — #413 and #414 each moved half
the family and neither could ever go green (see #425).

This does not contradict #398's decision to ungroup majors: that rationale was
"one migration per PR", and the Storybook family IS one migration whose packages
must move in lockstep. Listed first so Dependabot keeps the family together for
minor/patch too, which have the same lockstep requirement.

Kept as the last commit, touching only this file, so it can be dropped with a
single revert without touching the migration.
@Shironex
Shironex force-pushed the chore/storybook-10-migration branch from 40d422d to 59caa18 Compare August 1, 2026 00:46
@Shironex Shironex added the enhancement New feature or request label Aug 1, 2026
@Shironex
Shironex merged commit 3a48a1a into main Aug 1, 2026
13 checks passed
@Shironex
Shironex deleted the chore/storybook-10-migration branch August 1, 2026 00:54
@github-project-automation github-project-automation Bot moved this from Todo to Done in Nightcore Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: web React board (apps/web) dependencies Pull requests that update a dependency file enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Storybook 10 migration: the family must bump in lockstep + play-function Canvas API changed

1 participant