mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-07 07:21:17 +08:00
Core: MDX support for built-in stories.json
This commit is contained in:
parent
0b77e47f2a
commit
379921eb89
@ -21,6 +21,10 @@ module.exports = {
|
||||
},
|
||||
// These are just here to test composition. They could be added to any storybook example project
|
||||
refs: {
|
||||
react: {
|
||||
title: 'ReactTS',
|
||||
url: 'http://localhost:9011',
|
||||
},
|
||||
first: {
|
||||
title: 'Composition test one',
|
||||
url: 'https://storybookjs.netlify.app/cra-ts-essentials',
|
||||
|
@ -5,7 +5,7 @@ import userEvent from '@testing-library/user-event';
|
||||
import { AccountForm, AccountFormProps } from './AccountForm';
|
||||
|
||||
export default {
|
||||
title: 'AccountForm',
|
||||
title: 'Demo/AccountForm',
|
||||
component: AccountForm,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
|
9
examples/react-ts/src/addon-docs/docs-only.stories.mdx
Normal file
9
examples/react-ts/src/addon-docs/docs-only.stories.mdx
Normal file
@ -0,0 +1,9 @@
|
||||
import { Meta, Canvas } from '@storybook/addon-docs';
|
||||
|
||||
<Meta title="Docs/docs-only" parameters={{ previewTabs: { canvas: { hidden: true } } }} />
|
||||
|
||||
# Documentation-only MDX
|
||||
|
||||
## [Link](http://https://storybook.js.org/) in heading
|
||||
|
||||
This file is a documentation-only MDX file, i.e. it doesn't contain any `<Story>` definitions.
|
12
examples/react-ts/src/addon-docs/docs.stories.mdx
Normal file
12
examples/react-ts/src/addon-docs/docs.stories.mdx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Meta, Story } from '@storybook/addon-docs';
|
||||
import { Button } from '../button';
|
||||
|
||||
<Meta title="Docs/Button" component={Button} />
|
||||
|
||||
# Button
|
||||
|
||||
<Story name="Basic">
|
||||
<Button label="Click me" />
|
||||
</Story>
|
||||
|
||||
<Story name="Controls">{(args) => <Button {...args} />}</Story>
|
@ -3,7 +3,7 @@ import fs from 'fs-extra';
|
||||
import glob from 'globby';
|
||||
import { logger } from '@storybook/node-logger';
|
||||
import { resolvePathInStorybookCache, Options, normalizeStories } from '@storybook/core-common';
|
||||
import { readCsf } from '@storybook/csf-tools';
|
||||
import { readCsfOrMdx } from '@storybook/csf-tools';
|
||||
|
||||
// TODO -- use proper types for these? share with StoriesListStory?
|
||||
interface ExtractedStory {
|
||||
@ -15,7 +15,7 @@ interface ExtractedStory {
|
||||
type ExtractedStories = Record<string, ExtractedStory>;
|
||||
|
||||
export async function extractStoriesJson(
|
||||
ouputFile: string,
|
||||
outputFile: string,
|
||||
storiesGlobs: string[],
|
||||
configDir: string
|
||||
) {
|
||||
@ -36,12 +36,12 @@ export async function extractStoriesJson(
|
||||
storyFiles.map(async (absolutePath) => {
|
||||
const ext = path.extname(absolutePath);
|
||||
const relativePath = path.relative(configDir, absolutePath);
|
||||
if (!['.js', '.jsx', '.ts', '.tsx'].includes(ext)) {
|
||||
if (!['.js', '.jsx', '.ts', '.tsx', '.mdx'].includes(ext)) {
|
||||
logger.info(`Skipping ${ext} file ${relativePath}`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const csf = (await readCsf(absolutePath)).parse();
|
||||
const csf = (await readCsfOrMdx(absolutePath)).parse();
|
||||
csf.stories.forEach(({ id, name }) => {
|
||||
stories[id] = {
|
||||
title: csf.meta.title,
|
||||
@ -55,7 +55,7 @@ export async function extractStoriesJson(
|
||||
}
|
||||
})
|
||||
);
|
||||
await fs.writeJson(ouputFile, { v: 3, stories });
|
||||
await fs.writeJson(outputFile, { v: 3, stories });
|
||||
}
|
||||
|
||||
const timeout = 30000; // 30s
|
||||
|
@ -191,6 +191,29 @@ describe('CsfFile', () => {
|
||||
__id: foo-bar--a
|
||||
`);
|
||||
});
|
||||
|
||||
it('docs-only story', async () => {
|
||||
expect(
|
||||
await parse(
|
||||
dedent`
|
||||
export default { title: 'foo/bar' };
|
||||
export const __page = () => {};
|
||||
__page.parameters = { docsOnly: true };
|
||||
`,
|
||||
true
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
meta:
|
||||
title: foo/bar
|
||||
stories:
|
||||
- id: foo-bar--page
|
||||
name: __page
|
||||
parameters:
|
||||
__isArgsStory: false
|
||||
__id: foo-bar--page
|
||||
docsOnly: true
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
// NOTE: this does not have a public API, but we can still test it
|
||||
|
@ -265,10 +265,15 @@ export class CsfFile {
|
||||
|
||||
// default export can come at any point in the file, so we do this post processing last
|
||||
if (self._meta?.title || self._meta?.component) {
|
||||
self._stories = Object.entries(self._stories).reduce((acc, [key, story]) => {
|
||||
const entries = Object.entries(self._stories);
|
||||
self._stories = entries.reduce((acc, [key, story]) => {
|
||||
if (isExportStory(key, self._meta)) {
|
||||
const id = toId(self._meta.title, storyNameFromExport(key));
|
||||
acc[key] = { ...story, id, parameters: { ...story.parameters, __id: id } };
|
||||
const parameters: Record<string, any> = { ...story.parameters, __id: id };
|
||||
if (entries.length === 1 && key === '__page') {
|
||||
parameters.docsOnly = true;
|
||||
}
|
||||
acc[key] = { ...story, id, parameters };
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, Story>);
|
||||
|
@ -1 +1,15 @@
|
||||
import fs from 'fs-extra';
|
||||
import mdx from '@mdx-js/mdx';
|
||||
|
||||
import { loadCsf } from './CsfFile';
|
||||
import { createCompiler } from './mdx';
|
||||
|
||||
export const readCsfOrMdx = async (fileName: string) => {
|
||||
let code = (await fs.readFile(fileName, 'utf-8')).toString();
|
||||
if (fileName.endsWith('.mdx')) {
|
||||
code = await mdx(code, { compilers: [createCompiler({})] });
|
||||
}
|
||||
return loadCsf(code);
|
||||
};
|
||||
|
||||
export * from './CsfFile';
|
||||
|
1
lib/csf-tools/src/mdx/typings.d.ts
vendored
1
lib/csf-tools/src/mdx/typings.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
declare module '@mdx-js/react';
|
||||
declare module '@mdx-js/mdx';
|
||||
declare module '@mdx-js/mdx/mdx-hast-to-jsx';
|
||||
declare module 'js-string-escape';
|
||||
|
Loading…
x
Reference in New Issue
Block a user