Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Turn on autodocs for CSF with attached meta. #20867

Merged
merged 7 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- [From version 6.5.x to 7.0.0](#from-version-65x-to-700)
- [7.0 breaking changes](#70-breaking-changes)
- [Story context is prepared before for supporting fine grained updates](#story-context-is-prepared-before-for-supporting-fine-grained-updates)
- [Dropped support for Node 15 and below](#dropped-support-for-node-15-and-below)
- [ESM format in Main.js](#esm-format-in-mainjs)
- [Modern browser support](#modern-browser-support)
Expand All @@ -24,7 +25,7 @@
- [Addon-a11y: Removed deprecated withA11y decorator](#addon-a11y-removed-deprecated-witha11y-decorator)
- [Vite](#vite)
- [Vite builder uses Vite config automatically](#vite-builder-uses-vite-config-automatically)
- [Vite cache moved to node_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook)
- [Vite cache moved to node\_modules/.cache/.vite-storybook](#vite-cache-moved-to-node_modulescachevite-storybook)
- [Webpack](#webpack)
- [Webpack4 support discontinued](#webpack4-support-discontinued)
- [Postcss removed](#postcss-removed)
Expand Down Expand Up @@ -63,7 +64,7 @@
- [Dropped addon-docs manual babel configuration](#dropped-addon-docs-manual-babel-configuration)
- [Dropped addon-docs manual configuration](#dropped-addon-docs-manual-configuration)
- [Autoplay in docs](#autoplay-in-docs)
- [Removed STORYBOOK_REACT_CLASSES global](#removed-storybook_react_classes-global)
- [Removed STORYBOOK\_REACT\_CLASSES global](#removed-storybook_react_classes-global)
- [7.0 Deprecations and default changes](#70-deprecations-and-default-changes)
- [storyStoreV7 enabled by default](#storystorev7-enabled-by-default)
- [`Story` type deprecated](#story-type-deprecated)
Expand Down Expand Up @@ -869,7 +870,7 @@ import * as ComponentStories from './some-component.stories';
<Story of={ComponentStories.Primary} />
```

You can create as many docs entries as you like for a given component. Note that if you attach a docs entry to a component it will replace the automatically generated entry from Autodocs.
You can create as many docs entries as you like for a given component. By default the docs entry will be named the same as the `.mdx` file (e.g. `Introduction.mdx` becomes `Introduction`). If the docs file is named the same as the component (e.g. `Button.mdx`, it will use the default autodocs name (`"Docs"`) and override autodocs).

By default docs entries are listed first for the component. You can sort them using story sorting.

Expand Down
2 changes: 1 addition & 1 deletion code/addons/docs/template/stories/docs2/MetaOf.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as ButtonStories from './button.stories.ts';

<Meta of={ButtonStories} />

# Docs with of
# Docs with of, but no name

hello docs

Expand Down
12 changes: 12 additions & 0 deletions code/addons/docs/template/stories/docs2/MetaOfNamed.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Meta, Story, Stories } from '@storybook/addon-docs';
import * as ButtonStories from './button.stories.ts';

<Meta of={ButtonStories} name="Doc Name" />

# Docs with of, and name

hello docs

<Story of={ButtonStories.Basic} />

<Stories />
2 changes: 1 addition & 1 deletion code/addons/docs/template/stories/docs2/NoTitle.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Docs with no title
# Unattached docs with no title

hello docs
1 change: 1 addition & 0 deletions code/addons/docs/template/stories/docs2/button.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { global as globalThis } from '@storybook/global';

export default {
component: globalThis.Components.Button,
tags: ['autodocs'],
args: { onClick: () => console.log('clicked!') },
parameters: {
chromatic: { disable: true },
Expand Down
188 changes: 167 additions & 21 deletions code/lib/core-server/src/utils/StoryIndexGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,33 +463,169 @@ describe('StoryIndexGenerator', () => {
);
});

it('throws an error if you attach a MetaOf entry to a tagged autodocs entry', async () => {
it('throws an error if you attach a named MetaOf entry which clashes with a tagged autodocs entry', async () => {
const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/B.stories.ts',
options
);

const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./errors/MetaOfAutodocs.mdx',
'./errors/MetaOfClashingDefaultName.mdx',
options
);

const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions);
await generator.initialize();

await expect(generator.getIndex()).rejects.toThrowError(
`You created a component docs page for B (./errors/MetaOfAutodocs.mdx), but also tagged the CSF file (./src/B.stories.ts) with 'autodocs'. This is probably a mistake.`
`You created a component docs page for B (./errors/MetaOfClashingDefaultName.mdx), but also tagged the CSF file (./src/B.stories.ts) with 'autodocs'. This is probably a mistake.`
);
});

it('throws an error if you attach a unnamed MetaOf entry with the same name as the CSF file that clashes with a tagged autodocs entry', async () => {
const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/B.stories.ts',
options
);

const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./errors/B.mdx',
options
);

const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions);
await generator.initialize();

await expect(generator.getIndex()).rejects.toThrowError(
`You created a component docs page for B (./errors/B.mdx), but also tagged the CSF file (./src/B.stories.ts) with 'autodocs'. This is probably a mistake.`
);
});

it('allows you to create a second unnamed MetaOf entry that does not clash with autodocs', async () => {
const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/B.stories.ts',
options
);

const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./errors/MetaOfNoName.mdx',
options
);

const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions);
await generator.initialize();

expect(await generator.getIndex()).toMatchInlineSnapshot(`
Object {
"entries": Object {
"b--docs": Object {
"id": "b--docs",
"importPath": "./src/B.stories.ts",
"name": "docs",
"storiesImports": Array [],
"tags": Array [
"autodocs",
"docs",
],
"title": "B",
"type": "docs",
},
"b--metaofnoname": Object {
"id": "b--metaofnoname",
"importPath": "./errors/MetaOfNoName.mdx",
"name": "MetaOfNoName",
"storiesImports": Array [
"./src/B.stories.ts",
],
"tags": Array [
"docs",
],
"title": "B",
"type": "docs",
},
"b--story-one": Object {
"id": "b--story-one",
"importPath": "./src/B.stories.ts",
"name": "Story One",
"tags": Array [
"autodocs",
"story",
],
"title": "B",
"type": "story",
},
},
"v": 4,
}
`);
});
it('allows you to create a second MetaOf entry with a different name to autodocs', async () => {
const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/B.stories.ts',
options
);

const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./errors/MetaOfName.mdx',
options
);

const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions);
await generator.initialize();

expect(await generator.getIndex()).toMatchInlineSnapshot(`
Object {
"entries": Object {
"b--docs": Object {
"id": "b--docs",
"importPath": "./src/B.stories.ts",
"name": "docs",
"storiesImports": Array [],
"tags": Array [
"autodocs",
"docs",
],
"title": "B",
"type": "docs",
},
"b--name": Object {
"id": "b--name",
"importPath": "./errors/MetaOfName.mdx",
"name": "name",
"storiesImports": Array [
"./src/B.stories.ts",
],
"tags": Array [
"docs",
],
"title": "B",
"type": "docs",
},
"b--story-one": Object {
"id": "b--story-one",
"importPath": "./src/B.stories.ts",
"name": "Story One",
"tags": Array [
"autodocs",
"story",
],
"title": "B",
"type": "story",
},
},
"v": 4,
}
`);
});

it('allows you to override autodocs with MetaOf if it is automatic', async () => {
const csfSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/A.stories.js',
options
);

const docsSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./src/docs2/MetaOf.mdx',
'./errors/A.mdx',
options
);

Expand All @@ -504,7 +640,7 @@ describe('StoryIndexGenerator', () => {
"entries": Object {
"a--docs": Object {
"id": "a--docs",
"importPath": "./src/docs2/MetaOf.mdx",
"importPath": "./errors/A.mdx",
"name": "docs",
"storiesImports": Array [
"./src/A.stories.js",
Expand Down Expand Up @@ -613,10 +749,10 @@ describe('StoryIndexGenerator', () => {
expect(await generator.getIndex()).toMatchInlineSnapshot(`
Object {
"entries": Object {
"a--docs": Object {
"id": "a--docs",
"a--metaof": Object {
"id": "a--metaof",
"importPath": "./src/docs2/MetaOf.mdx",
"name": "docs",
"name": "MetaOf",
"storiesImports": Array [
"./src/A.stories.js",
],
Expand Down Expand Up @@ -748,10 +884,10 @@ describe('StoryIndexGenerator', () => {
expect(await generator.getIndex()).toMatchInlineSnapshot(`
Object {
"entries": Object {
"a--info": Object {
"id": "a--info",
"a--metaof": Object {
"id": "a--metaof",
"importPath": "./src/docs2/MetaOf.mdx",
"name": "Info",
"name": "MetaOf",
"storiesImports": Array [
"./src/A.stories.js",
],
Expand Down Expand Up @@ -829,7 +965,7 @@ describe('StoryIndexGenerator', () => {
describe('duplicates', () => {
it('warns when two MDX entries reference the same CSF file without a name', async () => {
const docsErrorSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./errors/DuplicateMetaOf.mdx',
'./errors/**/A.mdx',
options
);

Expand All @@ -842,10 +978,11 @@ describe('StoryIndexGenerator', () => {
expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
Array [
"a--story-one",
"a--docs",
"a--metaof",
"notitle--docs",
"a--second-docs",
"docs2-yabbadabbadooo--docs",
"a--docs",
]
`);

Expand All @@ -870,7 +1007,7 @@ describe('StoryIndexGenerator', () => {
expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
Array [
"a--story-one",
"a--docs",
"a--metaof",
"notitle--docs",
"a--second-docs",
"docs2-yabbadabbadooo--docs",
Expand All @@ -884,15 +1021,24 @@ describe('StoryIndexGenerator', () => {
});

it('warns when a story has the default docs name', async () => {
const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], {
...options,
docs: { ...options.docs, defaultName: 'Story One' },
});
const docsErrorSpecifier: NormalizedStoriesSpecifier = normalizeStoriesEntry(
'./errors/A.mdx',
options
);

const generator = new StoryIndexGenerator(
[storiesSpecifier, docsSpecifier, docsErrorSpecifier],
{
...options,
docs: { ...options.docs, defaultName: 'Story One' },
}
);
await generator.initialize();

expect(Object.keys((await generator.getIndex()).entries)).toMatchInlineSnapshot(`
Array [
"a--story-one",
"a--metaof",
"notitle--story-one",
"a--second-docs",
"docs2-yabbadabbadooo--story-one",
Expand Down Expand Up @@ -952,7 +1098,7 @@ describe('StoryIndexGenerator', () => {
"d--story-one",
"b--story-one",
"nested-button--story-one",
"a--docs",
"a--metaof",
"a--second-docs",
"a--story-one",
"second-nested-g--story-one",
Expand Down Expand Up @@ -1200,11 +1346,11 @@ describe('StoryIndexGenerator', () => {
await generator.getIndex();
expect(toId).toHaveBeenCalledTimes(5);

expect(Object.keys((await generator.getIndex()).entries)).toContain('a--docs');
expect(Object.keys((await generator.getIndex()).entries)).toContain('a--metaof');

generator.invalidate(docsSpecifier, './src/docs2/MetaOf.mdx', true);

expect(Object.keys((await generator.getIndex()).entries)).not.toContain('a--docs');
expect(Object.keys((await generator.getIndex()).entries)).not.toContain('a--metaof');

// this will throw if MetaOf is not removed from A's dependents
generator.invalidate(storiesSpecifier, './src/A.stories.js', false);
Expand Down
Loading