diff --git a/README.md b/README.md index ea0d498..e71ab72 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ This plugin does not support MDX files. | [`storybook/csf-component`](./docs/rules/csf-component.md) | The component property should be set | | | | [`storybook/default-exports`](./docs/rules/default-exports.md) | Story files should have a default export | 🔧 | | | [`storybook/hierarchy-separator`](./docs/rules/hierarchy-separator.md) | Deprecated hierarchy separator in title property | 🔧 | | +| [`storybook/meta-inline-properties`](./docs/rules/meta-inline-properties.md) | Meta should only have inline properties | | N/A | | [`storybook/no-redundant-story-name`](./docs/rules/no-redundant-story-name.md) | A story should not have a redundant name property | 🔧 | | | [`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 | 🔧 | | diff --git a/docs/rules/await-interactions.md b/docs/rules/await-interactions.md index b89cea0..3f34ec0 100644 --- a/docs/rules/await-interactions.md +++ b/docs/rules/await-interactions.md @@ -8,12 +8,12 @@ ## Rule Details -Storybook provides an instrumented version of testing library in the [@storybook/testing-library](https://github.com/storybookjs/testing-library/) package. When [writing interactions](https://storybook.js.org/docs/react/essentials/interactions), make sure to **await** them, so that addon-interactions can intercept these helper functions and allow you to step through them when debugging. +Storybook provides an instrumented version of testing library in the [@storybook/test](https://github.com/storybookjs/storybook/tree/next/code/lib/test) library (formerly available in [@storybook/testing-library](https://github.com/storybookjs/testing-library/) library). When [writing interactions](https://storybook.js.org/docs/essentials/interactions), make sure to **await** them, so that addon-interactions can intercept these helper functions and allow you to step through them when debugging. Examples of **incorrect** code for this rule: ```js -import { within, userEvent } from '@storybook/testing-library' +import { within, userEvent } from '@storybook/test' // or from the legacy package "@storybook/testing-library"; MyStory.play = (context) => { const canvas = within(context.canvasElement) @@ -25,7 +25,7 @@ MyStory.play = (context) => { Examples of **correct** code for this rule: ```js -import { within, userEvent } from '@storybook/testing-library' +import { within, userEvent } from '@storybook/test' // or from the legacy package "@storybook/testing-library"; MyStory.play = async (context) => { const canvas = within(context.canvasElement) diff --git a/docs/rules/csf-component.md b/docs/rules/csf-component.md index 5cbfb1f..2a71b7f 100644 --- a/docs/rules/csf-component.md +++ b/docs/rules/csf-component.md @@ -33,4 +33,4 @@ While we encourage each CSF file to clearly correspond to a single component, it ## Further Reading -- [Automatic argType inference](https://storybook.js.org/docs/react/api/argtypes#automatic-argtype-inference) +- [Automatic argType inference](https://storybook.js.org/docs/api/argtypes#automatic-argtype-inference) diff --git a/docs/rules/default-exports.md b/docs/rules/default-exports.md index 557af7b..7de9d6c 100644 --- a/docs/rules/default-exports.md +++ b/docs/rules/default-exports.md @@ -8,7 +8,7 @@ ## Rule Details -In [CSF](https://storybook.js.org/docs/react/writing-stories/introduction#component-story-format), a story file should contain a _default export_ that describes the component, and _named exports_ that describe the stories. This rule enforces the definition of a default export in story files. +In [CSF](https://storybook.js.org/docs/writing-stories#component-story-format), a story file should contain a _default export_ that describes the component, and _named exports_ that describe the stories. This rule enforces the definition of a default export in story files. Examples of **incorrect** code for this rule: @@ -36,4 +36,4 @@ If you're using Storybook 6.5 and [CSF in MDX](https://github.com/storybookjs/st ## Further Reading -More information about defining stories here: https://storybook.js.org/docs/react/writing-stories/introduction#defining-stories +More information about defining stories here: https://storybook.js.org/docs/writing-stories#defining-stories diff --git a/docs/rules/meta-inline-properties.md b/docs/rules/meta-inline-properties.md index 0ba7df3..d962b37 100644 --- a/docs/rules/meta-inline-properties.md +++ b/docs/rules/meta-inline-properties.md @@ -1,6 +1,9 @@ # Meta should only have inline properties (meta-inline-properties) + +**Included in these configurations**: N/A + ## Rule Details diff --git a/docs/rules/no-stories-of.md b/docs/rules/no-stories-of.md index ec61c1a..3d5280f 100644 --- a/docs/rules/no-stories-of.md +++ b/docs/rules/no-stories-of.md @@ -8,7 +8,7 @@ ## Rule Details -Since Storybook 5.2, the [CSF format](https://storybook.js.org/docs/react/api/csf) was introduced and the `storiesOf` API has been deprecated. +Since Storybook 5.2, the [CSF format](https://storybook.js.org/docs/api/csf) was introduced and the `storiesOf` API has been deprecated. Examples of **incorrect** code for this rule: diff --git a/docs/rules/prefer-pascal-case.md b/docs/rules/prefer-pascal-case.md index be0423f..1612edf 100644 --- a/docs/rules/prefer-pascal-case.md +++ b/docs/rules/prefer-pascal-case.md @@ -8,7 +8,7 @@ ## Rule Details -As a best practice, stories should be defined in [_PascalCase_](https://en.wiktionary.org/wiki/Pascal_case). This makes it simpler to visually differ stories to other code. Plus, it makes it simpler to define regexes for [non-story exports](https://storybook.js.org/docs/react/api/csf#non-story-exports). +As a best practice, stories should be defined in [_PascalCase_](https://en.wiktionary.org/wiki/Pascal_case). This makes it simpler to visually differ stories to other code. Plus, it makes it simpler to define regexes for [non-story exports](https://storybook.js.org/docs/api/csf#non-story-exports). Examples of **incorrect** code for this rule: @@ -24,4 +24,4 @@ export const PrimaryButton = {} ## Further Reading -More information about naming stories can be found here: https://storybook.js.org/docs/react/writing-stories/introduction#defining-stories +More information about naming stories can be found here: https://storybook.js.org/docs/writing-stories#defining-stories diff --git a/docs/rules/story-exports.md b/docs/rules/story-exports.md index df63c9d..ae4f95e 100644 --- a/docs/rules/story-exports.md +++ b/docs/rules/story-exports.md @@ -8,7 +8,7 @@ ## Rule Details -In [CSF](https://storybook.js.org/docs/react/writing-stories/introduction#component-story-format), a story file should contain a _default export_ that describes the component, and at _named exports_ that describe the stories. This rule enforces the definition of at least one named export in story files. +In [CSF](https://storybook.js.org/docs/writing-stories#component-story-format), a story file should contain a _default export_ that describes the component, and at _named exports_ that describe the stories. This rule enforces the definition of at least one named export in story files. Examples of **incorrect** code for this rule: @@ -38,4 +38,4 @@ This rule should only be applied in your `.stories.*` files. Please ensure you a ## Further Reading -More information about defining stories here: https://storybook.js.org/docs/react/writing-stories/introduction#defining-stories +More information about defining stories here: https://storybook.js.org/docs/writing-stories#defining-stories diff --git a/docs/rules/use-storybook-expect.md b/docs/rules/use-storybook-expect.md index eb772fd..a8ec3d7 100644 --- a/docs/rules/use-storybook-expect.md +++ b/docs/rules/use-storybook-expect.md @@ -8,26 +8,29 @@ ## Rule Details -Storybook provides a browser compatible version of Jest's expect via the [@storybook/jest](https://github.com/storybookjs/jest) library. -When [writing interactions](https://storybook.js.org/docs/react/essentials/interactions) and asserting values, you should always use `expect` from the `@storybook/jest` library. +Storybook provides a browser compatible version of `expect` via the [@storybook/test](https://github.com/storybookjs/storybook/tree/next/code/lib/test) library (formerly available in the legacy [@storybook/jest](https://github.com/storybookjs/jest) library). +When [writing interactions](https://storybook.js.org/docs/essentials/interactions) and asserting values, you should always use `expect` from the `@storybook/test` library. Examples of **incorrect** code for this rule: ```js -Default.play = () => { +Default.play = async () => { // using global expect from Jest. Will break on the browser - expect(123).toEqual(123) + await expect(123).toEqual(123) } ``` Examples of **correct** code for this rule: ```js +// correct import. +import { expect } from '@storybook/test' +// or this, which is now considered legacy import { expect } from '@storybook/jest' -Default.play = () => { +Default.play = async () => { // using imported expect from storybook package - expect(123).toEqual(123) + await expect(123).toEqual(123) } ``` diff --git a/docs/rules/use-storybook-testing-library.md b/docs/rules/use-storybook-testing-library.md index 1ba500a..0abdfd3 100644 --- a/docs/rules/use-storybook-testing-library.md +++ b/docs/rules/use-storybook-testing-library.md @@ -8,8 +8,8 @@ ## Rule Details -Storybook provides an instrumented version of testing library in the [@storybook/testing-library](https://github.com/storybookjs/testing-library/) package. -When [writing interactions](https://storybook.js.org/docs/react/essentials/interactions), make sure to use the helper functions from `@storybook/testing-library`, so that addon-interactions can intercept these helper functions and allow you to step through them when debugging. +Storybook provides an instrumented version of testing library in the [@storybook/test](https://github.com/storybookjs/storybook/tree/next/code/lib/test) library (formerly available in [@storybook/testing-library](https://github.com/storybookjs/testing-library/) library). +When [writing interactions](https://storybook.js.org/docs/essentials/interactions), make sure to use the helper functions from `@storybook/test`, so that addon-interactions can intercept these helper functions and allow you to step through them when debugging. Examples of **incorrect** code for this rule: @@ -26,6 +26,8 @@ Examples of **correct** code for this rule: ```js // correct import. +import { within } from '@storybook/test' +// or this, which is now considered legacy import { within } from '@storybook/testing-library' Default.play = async (context) => { diff --git a/lib/index.ts b/lib/index.ts index be3f92e..5fac0ab 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -19,6 +19,7 @@ import contextInPlayFunction from './rules/context-in-play-function' import csfComponent from './rules/csf-component' import defaultExports from './rules/default-exports' import hierarchySeparator from './rules/hierarchy-separator' +import metaInlineProperties from './rules/meta-inline-properties' import noRedundantStoryName from './rules/no-redundant-story-name' import noStoriesOf from './rules/no-stories-of' import noTitlePropertyInMeta from './rules/no-title-property-in-meta' @@ -49,6 +50,7 @@ export = { 'csf-component': csfComponent, 'default-exports': defaultExports, 'hierarchy-separator': hierarchySeparator, + 'meta-inline-properties': metaInlineProperties, 'no-redundant-story-name': noRedundantStoryName, 'no-stories-of': noStoriesOf, 'no-title-property-in-meta': noTitlePropertyInMeta, diff --git a/tests/integrations/flat-config.spec.ts b/tests/integrations/flat-config.spec.ts index 8a9f43e..2aad67a 100644 --- a/tests/integrations/flat-config.spec.ts +++ b/tests/integrations/flat-config.spec.ts @@ -30,11 +30,12 @@ describe('Integration with flat config', () => { } const result = JSON.parse( - cp.execSync(`${ESLINT} a.stories.tsx --max-warnings 1 --format=json`, { + cp.execSync(`${ESLINT} a.stories.tsx --max-warnings 2 --format=json`, { encoding: 'utf-8', }) ) expect(result.length).toBe(1) expect(result[0].messages[0].messageId).toBe('shouldHaveStoryExport') + expect(result[0].messages[1].messageId).toBe('metaShouldHaveInlineProperties') }) }) diff --git a/tests/integrations/flat-config/a.stories.tsx b/tests/integrations/flat-config/a.stories.tsx index 0d5133e..b07c30c 100644 --- a/tests/integrations/flat-config/a.stories.tsx +++ b/tests/integrations/flat-config/a.stories.tsx @@ -1,6 +1,4 @@ const Input = () => 'This is an input component' -export default { - title: 'Input', - component: Input, -} +const title = 'foo'; +export default { title } diff --git a/tests/integrations/flat-config/eslint.config.js b/tests/integrations/flat-config/eslint.config.js index 02feb2c..e584222 100644 --- a/tests/integrations/flat-config/eslint.config.js +++ b/tests/integrations/flat-config/eslint.config.js @@ -6,6 +6,7 @@ export default [ files: ['**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)', '**/*.story.@(ts|tsx|js|jsx|mjs|cjs)'], rules: { 'storybook/story-exports': 'warn', + 'storybook/meta-inline-properties': 'warn', }, }, ] diff --git a/tests/integrations/legacy-config.spec.ts b/tests/integrations/legacy-config.spec.ts index f441480..2bb9e8c 100644 --- a/tests/integrations/legacy-config.spec.ts +++ b/tests/integrations/legacy-config.spec.ts @@ -30,11 +30,12 @@ describe('Integration with legacy config', () => { } const result = JSON.parse( - cp.execSync(`${ESLINT} a.stories.tsx --max-warnings 1 --format=json`, { + cp.execSync(`${ESLINT} a.stories.tsx --max-warnings 2 --format=json`, { encoding: 'utf-8', }) ) expect(result.length).toBe(1) expect(result[0].messages[0].messageId).toBe('shouldHaveStoryExport') + expect(result[0].messages[1].messageId).toBe('metaShouldHaveInlineProperties') }) }) diff --git a/tests/integrations/legacy-config/.eslintrc.json b/tests/integrations/legacy-config/.eslintrc.json index 171b300..2c0fa80 100644 --- a/tests/integrations/legacy-config/.eslintrc.json +++ b/tests/integrations/legacy-config/.eslintrc.json @@ -9,7 +9,8 @@ { "files": ["*.stories.@(ts|tsx|js|jsx|mjs|cjs)"], "rules": { - "storybook/story-exports": "warn" + "storybook/story-exports": "warn", + "storybook/meta-inline-properties": "warn" } } ] diff --git a/tests/integrations/legacy-config/a.stories.tsx b/tests/integrations/legacy-config/a.stories.tsx index 0d5133e..b07c30c 100644 --- a/tests/integrations/legacy-config/a.stories.tsx +++ b/tests/integrations/legacy-config/a.stories.tsx @@ -1,6 +1,4 @@ const Input = () => 'This is an input component' -export default { - title: 'Input', - component: Input, -} +const title = 'foo'; +export default { title } diff --git a/tools/update-rules-list.ts b/tools/update-rules-list.ts index c6eb54d..2d3f9d6 100644 --- a/tools/update-rules-list.ts +++ b/tools/update-rules-list.ts @@ -28,11 +28,13 @@ const rulesList: TRulesList[] = Object.entries(rules) return [ rule.name, createRuleLink(rule.name), - rule.meta.docs.description, + rule.meta.docs?.description || '', rule.meta.fixable ? emojiKey.fixable : '', - rule.meta.docs.categories - ? `` - : '', + rule.meta.docs?.excludeFromConfig + ? 'N/A' + : rule.meta.docs?.categories + ? `` + : '', ] }) diff --git a/tools/utils/categories.ts b/tools/utils/categories.ts index 775e74b..29e5d4e 100644 --- a/tools/utils/categories.ts +++ b/tools/utils/categories.ts @@ -24,13 +24,13 @@ for (const categoryId of categoryIds) { categoriesConfig[categoryId].rules = [] for (const rule of rules) { - const ruleCategories = rule.meta.docs.categories + const ruleCategories = rule.meta.docs?.categories // Throw if rule does not have a category if (!ruleCategories?.length) { throw new Error(`Rule "${rule.ruleId}" does not have any category.`) } - if (ruleCategories.includes(categoryId)) { + if (ruleCategories.includes(categoryId) && rule.meta.docs?.excludeFromConfig !== true) { categoriesConfig[categoryId].rules?.push(rule) } } diff --git a/tools/utils/docs.ts b/tools/utils/docs.ts index a4f6e66..d734879 100644 --- a/tools/utils/docs.ts +++ b/tools/utils/docs.ts @@ -106,7 +106,7 @@ export const updateRulesDocs = async (rulesList: TRulesList[]) => { const updatedDocFile = await format(overWriteRuleDocs(rule, ruleDocFile), { parser: 'markdown', - ...prettierConfig, + ...(await prettierConfig), }) await writeFile(ruleDocFilePath, updatedDocFile) diff --git a/tools/utils/rules.ts b/tools/utils/rules.ts index 6b1c2fa..e2f2ace 100644 --- a/tools/utils/rules.ts +++ b/tools/utils/rules.ts @@ -28,8 +28,6 @@ const rules = fs meta, } }) - // We might have rules which are almost ready but should not be shipped - .filter((rule) => !rule.meta.docs?.excludeFromConfig) export type TRules = typeof rules