Core: MDX support for built-in stories.json

This commit is contained in:
Michael Shilman 2021-08-10 19:15:46 +08:00
parent 0b77e47f2a
commit 379921eb89
9 changed files with 76 additions and 8 deletions

View File

@ -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',

View File

@ -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',

View 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.

View 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>

View File

@ -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

View File

@ -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

View File

@ -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>);

View File

@ -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';

View File

@ -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';