chore(storybook): enforce runnable "Show code" SFC via shared helper#688
Open
HerbertJulio wants to merge 3 commits into
Open
chore(storybook): enforce runnable "Show code" SFC via shared helper#688HerbertJulio wants to merge 3 commits into
HerbertJulio wants to merge 3 commits into
Conversation
The Docs "Show code" panel emitted snippets that could not be pasted and run: Storybook's dynamic Vue source lowercases the tag (`<skeleton>` instead of `<Skeleton>`) and a naive transform wrapped it in a nested `<template>`. The snippet must express 100% of the canvas and run as-is in a consumer app. - add apps/storybook/src/stories/_shared/story-source.js (toSfc, sfcTransform, runnableDocs): one sanctioned way to emit a single runnable PascalCase SFC; the transform unwraps the stray <template> and restores PascalCase tags - add .claude/rules/storybook-source.md: the well-defined rule - add .claude/hooks/validate-story-source.mjs (registered PreToolUse): blocks hand-rolled transforms, lowercase/kebab tags, nested <template>, and new stories that bypass the helper; only newly introduced violations block - update storybook-write skill + COMPONENT_REQUIREMENTS to mandate the helper - migrate Skeleton, Badge, Chip stories to runnableDocs / toSfc - cover the hook with tests in .claude/hooks/__tests__/run.mjs
…ource Root cause of the broken/inconsistent "Show code": Storybook's dynamic Vue source emits the tag from the .vue file name (skeleton.vue -> <skeleton>, chips.vue -> <chips>, not the PascalCase import), double-wraps <template>, and when `parameters.docs` is a function call it prints the raw CSF story object instead of the snippet. - story-source.js: reduce to a single `toSfc(imports, body)` SFC builder; drop the dynamic `sfcTransform` / `runnableDocs` path entirely - every story now declares an explicit `parameters.docs.source.code = toSfc(...)` with PascalCase tags; `parameters.docs` stays a plain object literal - Skeleton, Badge, Chip migrated to the explicit pattern (Chip <chips> fixed) - rule, skill, COMPONENT_REQUIREMENTS rewritten to the explicit pattern - hook now blocks: docs-as-function-call, any source.transform, lowercase/kebab tag, nested <template>, and (new files) missing toSfc / sourceState - hook tests updated
…ency The component shipped plural on every surface (chips.vue, ./chips, name 'Chips', testid input-chips) but the story imported it singular (`import Chip from '@aziontech/webkit/chips'`). A component must have ONE name across all surfaces. - rename to singular `chip`: folder/file, export ./chip, defineOptions name 'Chip', data-testid 'input-chip', spec chip.md (name + checksum), story import - new guardrail in validate-story-source.mjs: a story import binding must equal PascalCase(export subpath) — blocks `import Chip from '.../chips'` - .claude/rules/naming.md: the single-source-of-truth invariant (one kebab name -> spec, file, export, defineOptions, testid, story binding) and what enforces each surface (validate-spec-compliance for internals, validate-story-source for the story binding) - hook test for the binding mismatch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Docs "Show code" panel emitted snippets a consumer could not copy-paste and run. Storybook's dynamic Vue source has two defects:
<Skeleton>in the canvas is emitted as<skeleton>, which does not resolve to the imported component.<template>— a naivetransformwraps again, producing a nested<template><template>…</template></template>.Observed (real "Show code" before this PR):
The snippet must express 100% of what the canvas renders and run as-is.
What this PR does
A single sanctioned path for "Show code", impossible to bypass without the hook blocking it:
apps/storybook/src/stories/_shared/story-source.jsrunnableDocs({ component, imports, components })→ spread intoparameters.docs; setsdescription.component,source.transform,canvas.sourceState: 'shown'.sfcTransform→ unwraps the stray<template>and restores PascalCase tags.toSfc(imports, body)→ full SFC for composite stories'source.code..claude/rules/storybook-source.md: the well-defined contract (single runnable SFC, PascalCase tags, no hand-rolled transform, no nested<template>,sourceState: 'shown')..claude/hooks/validate-story-source.mjs(registered PreToolUse) blocks hand-rolled transforms, lowercase/kebab tags of imported components, nested<template>, and new stories that bypass the helper. Only newly introduced violations block, so legacy stories are migrated as touched.storybook-writeskill +COMPONENT_REQUIREMENTS§ 13 now mandate the helper (older "append the Usage block" guidance superseded).Skeleton,Badge,Chip..claude/hooks/__tests__/run.mjs(all pass).After:
Notes
lib: legacy whitelist,enforce-spec-exists) in files this PR does not touch; the 5 newvalidate-story-sourcetests pass.Dividerstory shipped in [ENG-46312] feat(webkit): add Divider (layout) #678 (that branch predates this helper; it will adopt the helper once it re-syncsdev).