diff --git a/README.md b/README.md index ec14616..22ed10c 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ This plugin does not support MDX files. | [`storybook/no-stories-of`](./docs/rules/no-stories-of.md) | storiesOf is deprecated and should not be used | | | | [`storybook/no-title-property-in-meta`](./docs/rules/no-title-property-in-meta.md) | Do not define a title in meta | 🔧 | | | [`storybook/no-uninstalled-addons`](./docs/rules/no-uninstalled-addons.md) | This rule identifies storybook addons that are invalid because they are either not installed or contain a typo in their name. | | | +| [`storybook/only-csf3`](./docs/rules/only-csf3.md) | Enforce Component Story Format 3.0 (CSF3) for stories | 🔧 | N/A | | [`storybook/prefer-pascal-case`](./docs/rules/prefer-pascal-case.md) | Stories should use PascalCase | 🔧 | | | [`storybook/story-exports`](./docs/rules/story-exports.md) | A story file must contain at least one story export | | | | [`storybook/use-storybook-expect`](./docs/rules/use-storybook-expect.md) | Use expect from `@storybook/test`, `storybook/test` or `@storybook/jest` | 🔧 | | diff --git a/docs/rules/only-csf3.md b/docs/rules/only-csf3.md new file mode 100644 index 0000000..22a6193 --- /dev/null +++ b/docs/rules/only-csf3.md @@ -0,0 +1,151 @@ +# Enforce CSF3 format for stories (only-csf3) + +[Component Story Format 3.0 (CSF3)](https://storybook.js.org/blog/component-story-format-3-0/) is the latest iteration of Storybook's story format, offering a simpler and more maintainable way to write stories. This rule enforces the use of CSF3 by identifying and reporting CSF2 patterns. + + + +**Included in these configurations**: N/A + + + +## Rule Details + +This rule aims to prevent the use of CSF2 patterns in story files and encourage migration to CSF3. + +Examples of **incorrect** code: + +```js +// ❌ CSF2: Using Template.bind({}) +const Template = (args) => + +// ❌ CSF2: Story with property assignments +export const WithArgs = Template.bind({}) +WithArgs.args = { label: 'With Args' } +WithArgs.parameters = { layout: 'centered' } + +// ❌ CSF2: Template.bind({}) with multiple stories +const Template = (args) => , +} + +// ✅ CSF3: Multiple stories sharing render logic +const render = (args) => +} + +// ✅ CSF3 +export const Primary = { + render: (args) => , +} +``` + +3. Story with multiple properties: + +```js +// ❌ CSF2 +export const Primary = Template.bind({}) +Primary.args = { label: 'Primary' } +Primary.parameters = { layout: 'centered' } +Primary.decorators = [ + (Story) => ( +
+ +
+ ), +] + +// ✅ CSF3 +export const Primary = { + render: (args) => + } + `, + output: dedent` + export const Secondary = { + render: function(args) { + return + }, + } + `, + errors: [ + { + messageId: 'noCSF2Format', + data: { + storyName: 'Secondary', + pattern: 'function expression', + }, + type: AST_NODE_TYPES.FunctionExpression, + }, + ], + }, + + // CSF2: Mixed with CSF3 (should detect both) + { + code: dedent` + export const Valid = { + args: { label: 'Valid' }, + } + export function Invalid(args) { + return