跳到正文
cccskills
免费在 GitHub 公开

component-stories

Use when creating or editing Storybook stories (*.stories.tsx, React or React Native) — story layout, controls, and export naming conventions.

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md18.6 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Storybook Story Guidelines

When creating or modifying Storybook stories, follow these conventions strictly:

Meta typing

const meta = {
  component: Component,
} satisfies Meta<typeof Component>;

export default meta;
type Story = StoryObj<typeof Component>;

Use satisfies Meta<typeof Component> (not a Meta<…> annotation) and StoryObj<typeof Component> (not typeof meta — that makes args required on every story).

Story Layout Configuration

Centering and Background

All stories must include these parameters:

export const Base: Story = {
  parameters: {
    layout: 'centered',
    backgrounds: { default: 'light' },
  },
  args: {
    // Component props
  },
};
  • Layout: Stories should be centered.
  • Background: Stories should use white background.

Docs source type

The snippet consumers (and AI agents) copy from must be real code. A snippet that prints icon={() => {}} is wrong and misleading. Storybook has three source types; pick the one that makes the snippet correct.

The three types

  • dynamic: serialises the rendered element tree. It expands values and shared consts (data={assets} becomes the array) and works for args and render stories. It cannot show component or icon references (icon={Plus} prints icon={() => {}}), functions whose body matters (renderItem, getRowProps), local helpers, or hooks, state, refs and local logic; those print as () => {} or disappear.
  • code: prints the story source exactly as written, so icons, functions, hooks and state all appear. It does not expand shared consts (data={assets} stays as written), and it prints the whole story object ({ render: () => … }, or { args: … } for args stories).
  • auto (type unset): Storybook's default. It behaves like dynamic for stories that take args and like code for render: () => … stories. Do not rely on it; set dynamic or code explicitly.

How to pick

Decide per story, then set the type on meta:

  1. Plain-data story (strings, numbers, booleans, objects, arrays, plain JSX) → dynamic. Write it with args (Controls work) or render.
  2. Story that needs real code → code. It passes a component, icon or other function-valued reference (icon={Plus}, leading={Spot}), a function whose body matters at any depth, a local helper or formatter, or it uses hooks, state, refs or local logic. "At any depth" means: a top-level prop (renderItem, getRowProps), a function nested in an object arg (yAxis: { tickLabelFormatter: (v) => … }, domain), a function used as a JSX child (a render prop such as {({ payload }) => …}), and a function that only exists in meta.args (e.g. formatter) and is inherited by the story. All of them print as () => {} under dynamic. Write it as render: () => <Button icon={Plus}>…</Button>, not args: { icon: Plus }, so the printed source is the usage.
  3. Trivial callbacks (onClick={() => {}}, onChange: () => console.log('…'), a bare fn()) do not count as functions that matter, unless the call is the point of the story (e.g. a Toast demo whose handler is toast.info(…)): then the story needs code.

Where to set the type: a per-story type: 'code' prints its own parameters block inside the snippet, and a per-story type: 'dynamic' does not. So never put type: 'code' on a story:

  • At least one story needs real code: meta type: 'code', and type: 'dynamic' on every plain story (even when that is most of the file).
  • Every story is plain data: meta type: 'dynamic', no overrides.

Base always keeps its args so the Controls table in the docs stays live and drives the preview, even when the printed code does not follow the controls. Do not replace <Controls of={Stories.Base} /> with <ArgTypes />, and do not turn Base into a render that ignores args.

  • Base needs an icon or component reference (icon: Settings): this is the one exception to "icon references are never dynamic". Keep Base args-based so the Controls stay live, and print it dynamic with a story-local lookup that turns the icon function back into its name, so the snippet is JSX with the real icon, not an { args } object and not icon={() => {}}:

    export const Base: Story = {
      parameters: {
        docs: { source: { type: 'dynamic' } },
        jsx: {
          functionValue: (fn: unknown) => Object.entries(iconMap).find(([, icon]) => icon === fn)?.[0] ?? '() => {}',
        },
      },
      args: { icon: Settings, children: 'Settings' },
    };
    

    iconMap is the map the story file already uses for the icon control (mapping), or a one-entry object like { Settings }. For a named local helper (e.g. a formatter), fn.name is enough: functionValue: (fn: { name?: string }) => fn.name || '() => {}'. Use the same lookup in both libs so the snippets match. Nothing is hand-written, so it cannot drift. Use this only for Base; every other story that needs an icon is written as render JSX under code.

  • Base needs state or handlers (a controlled input): keep args and add render: (args) => { const [value, setValue] = useState(''); return <TextInput {...args} value={value} onChangeText={setValue} /> }, so the Controls still drive the preview and the snippet is the real source.

  • A code snippet never prints meta.args, so repeat any meta-level arg the story relies on (e.g. formatter) in the story's own args. A story that inherits a meta-level function arg is a code story, not a plain one, even though its own source shows no function.

A story must render the real component the consumer uses. A story-local wrapper component (e.g. TextInputStory, Container, HorizontalScrollTable) shows up by name in the snippet, which is wrong, so do not use one:

  • inline the real component and its state in render;
  • move pure layout (centering, max width) into meta.decorators so it stays out of the snippet;
  • a reused helper that is only a function (not a component, e.g. getInitials) may stay at module level.
const meta = {
  component: Component,
  parameters: {
    docs: {
      source: {
        language: 'tsx',
        format: true,
        type: 'dynamic', // or 'code' per the rule above
      },
    },
  },
} satisfies Meta<typeof Component>;

Never hand-write parameters.docs.source.code; it drifts from the render. Content a story cannot print (an integration example such as a router asChild link, several alternative usages) goes in the component's MDX Implementation tab as a fenced tsx block, not in the story.

Inline sample data

The docs snippet prints the JSX/args as written, so a reference to a module-level constant (data={assets}) shows consumers an unresolved identifier. The stories consumers copy from must therefore carry their data inline:

  • Inline the data in Base and in any feature story that teaches an API (e.g. WithRenderItem), in its args or render, so the snippet is copy-pasteable.
  • Showcase and variation stories (SizeShowcase, ShapeShowcase, WithOverflow, Disabled, …) may share a module-level sample const (assets) and derive from it (.slice(), .map()). They illustrate one property, and repeating the data (e.g. ten sizes) buries it.
  • Do not leave an unused args: { data } next to a render that ignores it; the render is the source of truth.

Controls

Prefer controls inferred from component prop types (react-docgen-typescript is configured in Storybook). Do not duplicate argTypes for basic props already described in types.ts (unions, booleans, strings).

  • Add a Base story with args and a render that consumes them — required for Controls to appear in docs.
  • Add manual argTypes only for overrides docgen cannot express (actions, select mappings, hiding props).

Controls must be set up for every component docs page:

  • The MDX has a <Controls of={Stories.Base} /> block (Overview tab, under "Properties"), except for components with no configurable props.
  • The bound story (Base) has args with the root component's main props, and its render, if it has one, takes (args) and applies them (e.g. {...args}), so changing a control changes the preview. Meta-level args alone are not enough if render ignores its parameter, and a composition Base (Table, Dialog, SideBar) still seeds the root component from args.
  • State seeded from an arg must follow later changes: useState(args.page) only reads the arg once, so add useEffect(() => { setPage(args.page); }, [args.page]);. Otherwise changing the control after the first click does nothing.
  • Spread the root args first and put locally controlled props after it (<SelectList {...args} value={value} onValueChange={…}>), so every other control (disabled, searchValue, …) still reaches the component. Do not pass only one arg (e.g. items={args.items}).
  • Defaults in args equal what the story rendered before, so adding controls does not change the preview.
  • Controls expose only the component's real API props (the root, and its subcomponents through meta.subcomponents). Never invent playground args (title, showBackButton, showCoinCapsule, …) and never type a story with a custom args type (StoryObj<{ title: string }>) to fake controls: the snippet then shows conditionals and placeholders no consumer would write. A composition whose root has no configurable props (e.g. NavBar, Subheader) keeps its Controls block with just the real props (children, className, …) and a plain render composition with literal content, no args. Do not leave an empty args: {} placeholder either.
  • Controls only control the root component (meta.component). Subcomponents listed in meta.subcomponents get extra tabs in the Controls block that always show "Setup controls"; that is expected and cannot be fixed, so only list real subcomponents. Never list the main component again in subcomponents: it adds a duplicate tab whose controls are always empty.

Story Export Names

To maintain consistency across our Storybook documentation, follow these naming rules:

1. Base Story

The default, most basic usage of the component.

  • Use: Base
  • Do not use: Default, Primary, Basic

2. Showcase Stories

Showcase stories demonstrate variations of a single property.

  • Use the pattern: {Property}Showcase
  • Do not use: States, AllStates, StatesShowcase

3. Feature-Specific Stories

Stories highlighting specific features.

  • Use: With{Feature} (e.g., WithIcon, WithTooltip)

4. Truncation / Responsiveness Stories

Stories that demonstrate how a component truncates or adapts to constrained space.

  • Use: ResponsivenessShowcase
  • Do not use: TruncateShowcase, Truncation, LongLabel

Comments

Do not add comments in *.stories.tsx — no JSDoc above stories, no // or /* */ explanations. Story intent belongs in MDX or the export name. Keep only a required lint directive (e.g. eslint-disable-next-line) when a suppression is unavoidable.

General Principles

  1. Consistency over creativity: Follow the patterns even if you think another name might be clearer
  2. Singular property names: Use SizeShowcase not SizesShowcase
  3. PascalCase: All story names use PascalCase (e.g., WithTooltip)
  4. Avoid ambiguity: Don't use generic names like Example1, Test, Demo

Review checks

Rules verifiable from a diff.

CheckApplies toDetectSkip
Base story named Default/Primary/Basic instead of Baseall storiesexport name—
Showcase/feature story off-conventionall storiesnot {Property}Showcase / With{Feature} / ResponsivenessShowcase—
Missing layout: 'centered' + backgrounds: { default: 'light' }all storiesBase parameters—
Base or a feature story (With{Feature}) reads its data from a module-level const instead of inlining itall storiesdata={<const>} / args: { data: <const> } in Base or With{Feature}{Property}Showcase and variation stories
type: 'dynamic' applied to a story with function props, hooks, state or handlersall storiesdocs.source.type: 'dynamic' (on meta or the story) where a story has use* hooks, component or icon references (icon={Settings}), or function props with a meaningful body (render*, get*, function children, multi-statement handlers, helpers)stories with plain-data props only
Hand-written docs.source.codeall storiesa source.code string—
Story renders a local wrapper component instead of the real oneall storiesrender or args story whose rendered JSX uses a story-local component (e.g. <TextInputStory … />, <Container>)— (inline it; move layout to meta.decorators)
Base lost its Controlsall storiesBase without args, or <ArgTypes of={…Base} /> / a render that ignores args on Base—
Controls not set up on a docs page.stories.tsx + .mdx.mdx without <Controls of={…Base} />, or a Controls block bound to a story with no args or a zero-parameter rendercomponent with no configurable props
Story with a nested function, function child or meta-inherited function printed dynamicall storiestype: 'dynamic' (own or inherited) on a story whose args hold { yAxis: { tickLabelFormatter: … } }, a {({ payload }) => …} child, or that relies on a function in meta.args—
Control state does not follow the argall storiesuseState(args.x) in a Base with no useEffect re-sync, or <Root items={args.items} …> instead of {...args}—
Invented playground argsall storiesStoryObj<{ … }> / StoryObj<Playground…> custom args types, or args that are not props of meta.component (showBackButton, title on a root with no title prop), or an empty args: {}the Icon name picker (icons are separate generated components, so there is no single component to control)
Main component listed in meta.subcomponentsall storiessubcomponents: { <Root>, … } where <Root> is also component—
Per-story type: 'code'all storiesparameters.docs.source.type: 'code' on a story (its snippet then prints the parameters block)— (set code on meta and dynamic on the plain stories)
argTypes duplicated for props docgen already infersall storiesmanual argTypes for plain unions/booleans/stringsoverrides docgen can't express (actions, select mappings, hiding)

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Creates and maintains Figma Code Connect files (`*.figma.tsx`) that map Figma components to code via the parser-based `figma.connect()` API. Use when the user mentions Code Connect, Figma component mapping, design-to-code translation, or asks to create/update .figma.tsx files.

中文简介正在准备中。当前显示原文说明。

LedgerHQ/lumen232026年10月17日 更新

Use when creating, naming, or placing a file or folder in libs/*, or when modifying or adding a file to an existing component (even when the barrel isn't touched) — component vs utility naming, the one-responsibility-per-file layout, when a folder needs an `index.ts` barrel (public API only), and the required set of files a component needs per lib. Load this before scaffolding or restructuring a component so the layout matches the codebase.

中文简介正在准备中。当前显示原文说明。

LedgerHQ/lumen232026年10月17日 更新

Use when designing or changing a component's public API, its composition, or its state model in libs/ui-react or libs/ui-rnative — layering (core vs internal vs primitives), BaseProps/Props splits, converting a component to compound / changing its composition with createSafeContext, controlled/uncontrolled state, prop-naming conventions, and cross-platform API parity. Load this before shaping a new component, changing its props, or refactoring its composition.

中文简介正在准备中。当前显示原文说明。

LedgerHQ/lumen232026年10月17日 更新

Use when writing or editing Storybook MDX docs (*.mdx) — the two-tab Overview/Implementation structure, story-backed `<Source>` examples, and doc table guidelines.

中文简介正在准备中。当前显示原文说明。

LedgerHQ/lumen232026年10月17日 更新

Use when building or styling a component in libs/ui-react or libs/ui-rnative — the cross-platform styling principles, plus routing to the platform mechanics: Tailwind + cva + cn on web, useStyleSheet + themeJS + lx on React Native. Load this before writing component styles.

中文简介正在准备中。当前显示原文说明。

LedgerHQ/lumen232026年10月17日 更新

Use when writing or editing component tests in libs/ui-react or libs/ui-rnative — shared structure and coverage conventions, plus the per-platform runner: Vitest + React Testing Library on web, Jest + React Native Testing Library on RN. Load this before writing tests.

中文简介正在准备中。当前显示原文说明。

LedgerHQ/lumen232026年10月17日 更新

查看 LedgerHQ 的全部技能

报告此技能的问题