Skip to content

Commit

Permalink
Merge pull request #19495 from storybookjs/shilman/upgrade-mdx2
Browse files Browse the repository at this point in the history
Addon-docs: Upgrade to MDXv2
  • Loading branch information
shilman authored Oct 19, 2022
2 parents 41b2917 + 75759c1 commit a5ff418
Show file tree
Hide file tree
Showing 28 changed files with 125 additions and 734 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ jobs:
name: Run @storybook/bench on repro
command: |
cd ../cra-bench
npx -p @storybook/bench@1.0.0--canary.12.7cccdee.0 sb-bench 'echo noop' --label cra
npx -p @storybook/bench@0.7.6--canary.760ff01.0 sb-bench 'echo noop' --label cra
- run:
name: prep artifacts
when: always
Expand Down
16 changes: 16 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
- [Docs Page](#docs-page)
- [Configuring the Docs Container](#configuring-the-docs-container)
- [External Docs](#external-docs)
- [MDX2 upgrade](#mdx2-upgrade)
- [Dropped addon-docs manual configuration](#dropped-addon-docs-manual-configuration)
- [From version 6.4.x to 6.5.0](#from-version-64x-to-650)
- [Vue 3 upgrade](#vue-3-upgrade)
- [React18 new root API](#react18-new-root-api)
Expand Down Expand Up @@ -735,6 +737,20 @@ export default function App({ Component, pageProps }) {
}
```

#### MDX2 upgrade

Storybook 7 Docs uses MDXv2 instead of MDXv1. This means an improved syntax, support for inline JS expression, and improved performance among [other benefits](https://mdxjs.com/blog/v2/).

If you use `.stories.mdx` files in your project, you may need to edit them since MDX2 contains [breaking changes](https://mdxjs.com/migrating/v2/#update-mdx-files).

We will update this section with specific pointers based on user feedback during the prerelease period and probably add an codemod to help streamline the upgrade before final 7.0 release.

As part of the upgrade we deleted the codemod `mdx-to-csf` and will be replacing it with a more sophisticated version prior to release.

#### Dropped addon-docs manual configuration

Storybook Docs 5.x shipped with instructions for how to manually configure webpack and storybook without the use of Storybook's "presets" feature. Over time, these docs went out of sync. Now in Storybook 7 we have removed support for manual configuration entirely.

## From version 6.4.x to 6.5.0

### Vue 3 upgrade
Expand Down
62 changes: 0 additions & 62 deletions code/addons/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ Read on to learn more:
- [Installation](#installation)
- [Be sure to check framework specific installation needs](#be-sure-to-check-framework-specific-installation-needs)
- [Preset options](#preset-options)
- [Manual configuration](#manual-configuration)
- [TypeScript configuration](#typescript-configuration)
- [More resources](#more-resources)

Expand Down Expand Up @@ -166,67 +165,6 @@ import Changelog from '../CHANGELOG.md';
<Changelog />
```

## Manual configuration

We recommend using the preset, which should work out of the box. If you don't want to use the preset, and prefer to configure "the long way" add the following configuration to `.storybook/main.js` (see comments inline for explanation):

```js
const createCompiler = require('@storybook/addon-docs/mdx-compiler-plugin');

module.exports = {
// 1. register the docs panel (as opposed to '@storybook/addon-docs' which
// will configure everything with a preset)
addons: ['@storybook/addon-docs/register'],
// 2. manually configure webpack, since you're not using the preset
webpackFinal: async (config) => {
config.module.rules.push({
// 2a. Load `.stories.mdx` / `.story.mdx` files as CSF and generate
// the docs page from the markdown
test: /\.(stories|story)\.mdx$/,
use: [
{
// Need to add babel-loader as dependency: `yarn add -D babel-loader`
loader: require.resolve('babel-loader'),
// may or may not need this line depending on your app's setup
options: {
plugins: ['@babel/plugin-transform-react-jsx'],
},
},
{
loader: '@mdx-js/loader',
options: {
compilers: [createCompiler({})],
},
},
],
});
// 2b. Run `source-loader` on story files to show their source code
// automatically in `DocsPage` or the `Source` doc block.
config.module.rules.push({
test: /\.(stories|story)\.[tj]sx?$/,
loader: require.resolve('@storybook/source-loader'),
exclude: [/node_modules/],
enforce: 'pre',
});
return config;
},
};
```

You'll also need to set up the docs parameter in `.storybook/preview.js`. This includes the `DocsPage` for rendering the page, a container, and various configuration options, such as `extractComponentDescription` for manually extracting a component description:

```js
import { addParameters } from '@storybook/react';
import { DocsPage, DocsContainer } from '@storybook/addon-docs';

addParameters({
docs: {
container: DocsContainer,
page: DocsPage,
},
});
```

## TypeScript configuration

As of SB6 [TypeScript is zero-config](https://storybook.js.org/docs/react/configure/typescript) and should work with SB Docs out of the box. For advanced configuration options, refer to the [Props documentation](https://github.com/storybookjs/storybook/tree/next/code/addons/docs/docs/props-tables.md).
Expand Down
7 changes: 0 additions & 7 deletions code/addons/docs/docs/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

- [Component Story Format (CSF) with DocsPage](#component-story-format-csf-with-docspage)
- [Pure MDX Stories](#pure-mdx-stories)
- [Mixed CSF / MDX Stories](#mixed-csf--mdx-stories)
- [CSF Stories with MDX Docs](#csf-stories-with-mdx-docs)
- [CSF Stories with arbitrary MDX](#csf-stories-with-arbitrary-mdx)
- [Mixing storiesOf with CSF/MDX](#mixing-storiesof-with-csfmdx)
Expand All @@ -30,12 +29,6 @@ If you want to intersperse longform documentation in your Storybook, for example

[MDX](mdx.md) is an alternative syntax to CSF that allows you to co-locate your stories and your documentation. Everything you can do in CSF, you can do in MDX. And if you're consuming it in [Webpack](https://webpack.js.org/), it exposes an _identical_ interface, so the two files are interchangeable. Some teams will choose to write all of their Storybook in MDX and never look back.

## Mixed CSF / MDX Stories

Can't decide between CSF and MDX? In transition? Or have you found that each format has its own use? There's nothing stopping you from keeping some of your stories in CSF and some in MDX. And if you want to migrate one way or another, the [csf-to-mdx and mdx-to-csf codemod migrations](https://github.com/storybookjs/storybook/blob/next/code/lib/codemod/README.md) can help.

The only limitation is that your exported titles (CSF: `default.title`, MDX `Meta.title`) should be unique across files. Loading will fail if there are duplicate titles.

## CSF Stories with MDX Docs

Perhaps you want to write your stories in CSF, but document them in MDX? Here's how to do that:
Expand Down
7 changes: 4 additions & 3 deletions code/addons/docs/jest-transform-mdx.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ const path = require('path');
const { ScriptTransformer } = require('@jest/transform');
const { dedent } = require('ts-dedent');

const { compileSync } = require('@storybook/mdx1-csf');
const { compileAsync } = require('@storybook/mdx2-csf');

module.exports = {
process(src, filename, config, { instrument }) {
async processAsync(src, filename, config, { instrument }) {
const code = await compileAsync(src, { skipCsf: false });
const result = dedent`
/* @jsx mdx */
import React from 'react'
import { mdx } from '@mdx-js/react'
${compileSync(src, { filepath: filename })}
${code}
`;

const extension = path.extname(filename);
Expand Down
9 changes: 2 additions & 7 deletions code/addons/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"dependencies": {
"@babel/plugin-transform-react-jsx": "^7.12.12",
"@jest/transform": "^26.6.2",
"@mdx-js/react": "^1.6.22",
"@mdx-js/react": "^2.1.5",
"@storybook/addons": "7.0.0-alpha.39",
"@storybook/api": "7.0.0-alpha.39",
"@storybook/blocks": "7.0.0-alpha.39",
Expand All @@ -61,7 +61,7 @@
"@storybook/csf": "0.0.2--canary.49.258942b.0",
"@storybook/csf-tools": "7.0.0-alpha.39",
"@storybook/docs-tools": "7.0.0-alpha.39",
"@storybook/mdx1-csf": "0.0.5-canary.13.9ce928a.0",
"@storybook/mdx2-csf": "0.1.0-next.0",
"@storybook/node-logger": "7.0.0-alpha.39",
"@storybook/postinstall": "7.0.0-alpha.39",
"@storybook/preview-web": "7.0.0-alpha.39",
Expand All @@ -77,18 +77,13 @@
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@storybook/mdx2-csf": "0.0.4-canary.14.04ffbe8.0",
"typescript": "~4.6.3"
},
"peerDependencies": {
"@storybook/mdx2-csf": "0.0.4-canary.14.04ffbe8.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"@storybook/mdx2-csf": {
"optional": true
},
"react": {
"optional": true
},
Expand Down
6 changes: 5 additions & 1 deletion code/addons/docs/src/DocsRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ export class DocsRenderer<TFramework extends AnyFramework> {
// Use a random key to force the container to re-render each time we call `renderDocs`
// TODO: do we still need this? It was needed for angular (legacy) inline rendering:
// https://github.com/storybookjs/storybook/pull/16149
const components = {
...defaultComponents,
...docsParameter?.components,
};
ReactDOM.render(
<MDXProvider components={defaultComponents}>
<MDXProvider components={components}>
<Docs key={Math.random()} context={context} docsParameter={docsParameter} />
</MDXProvider>,
element,
Expand Down
12 changes: 3 additions & 9 deletions code/addons/docs/src/preset.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fs from 'fs-extra';
import remarkSlug from 'remark-slug';
import remarkExternalLinks from 'remark-external-links';
import global from 'global';

import type { DocsOptions, IndexerOptions, Options, StoryIndexer } from '@storybook/core-common';
import { logger } from '@storybook/node-logger';
Expand Down Expand Up @@ -68,12 +67,9 @@ export async function webpack(
remarkPlugins: [remarkSlug, remarkExternalLinks],
};

const mdxVersion = global.FEATURES?.previewMdx2 ? 'MDX2' : 'MDX1';
logger.info(`Addon-docs: using ${mdxVersion}`);
logger.info(`Addon-docs: using MDX2`);

const mdxLoader = global.FEATURES?.previewMdx2
? require.resolve('@storybook/mdx2-csf/loader')
: require.resolve('@storybook/mdx1-csf/loader');
const mdxLoader = require.resolve('@storybook/mdx2-csf/loader');

// set `sourceLoaderOptions` to `null` to disable for manual configuration
const sourceLoader = sourceLoaderOptions
Expand Down Expand Up @@ -155,9 +151,7 @@ export const storyIndexers = async (indexers: StoryIndexer[] | null) => {
const mdxIndexer = async (fileName: string, opts: IndexerOptions) => {
let code = (await fs.readFile(fileName, 'utf-8')).toString();
// @ts-expect-error (Converted from ts-ignore)
const { compile } = global.FEATURES?.previewMdx2
? await import('@storybook/mdx2-csf')
: await import('@storybook/mdx1-csf');
const { compile } = await import('@storybook/mdx2-csf');
code = await compile(code, {});
return loadCsf(code, { ...opts, fileName }).parse();
};
Expand Down
1 change: 0 additions & 1 deletion code/addons/docs/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
declare module '@mdx-js/react';
declare module 'global';
declare module '@egoist/vue-to-react';
declare module 'remark-slug';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import globalThis from 'global';
<Meta
title="addons/docs/stories-mdx/play functions"
component={globalThis.Components.Button}
// FIXME: this is currently broken in the MDX compiler
play={() => console.log('component play')}
/>

Expand All @@ -13,16 +12,9 @@ import globalThis from 'global';
This file demonstrates defining stories inside MDX.

<Canvas>
<Story
name="Component Play"
args={{ label: 'Component Play' }}
/>
<Story name="Component Play" args={{ label: 'Component Play' }} />
</Canvas>

<Canvas>
<Story
name="Story Play"
args={{ label: 'Story Play' }}
play={() => console.log('story play')}
/>
<Story name="Story Play" args={{ label: 'Story Play' }} play={() => console.log('story play')} />
</Canvas>
1 change: 0 additions & 1 deletion code/examples/doc-blocks/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const config: StorybookConfig = {
buildStoriesJson: true,
babelModeV7: true,
warnOnLegacyHierarchySeparator: false,
previewMdx2: true,
breakingChangesV7: true,
},
framework: '@storybook/react-webpack5',
Expand Down
5 changes: 4 additions & 1 deletion code/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ module.exports = {
'^.+\\.[jt]sx?$': '<rootDir>/../scripts/utils/jest-transform-js.js',
'^.+\\.mdx$': '@storybook/addon-docs/jest-transform-mdx',
},
transformIgnorePatterns: ['/node_modules/(?!lit-html).+\\.js'],
transformIgnorePatterns: [
'/node_modules/(?!(lit-html|@mdx-js)).+\\.js',
'/node_modules/(?!).+\\.js',
],
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
testPathIgnorePatterns: [
'/storybook-static/',
Expand Down
1 change: 1 addition & 0 deletions code/lib/blocks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@storybook/preview-web": "7.0.0-alpha.39",
"@storybook/store": "7.0.0-alpha.39",
"@storybook/theming": "7.0.0-alpha.39",
"@types/lodash": "^4.14.167",
"color-convert": "^2.0.1",
"dequal": "^2.0.2",
"global": "^4.4.0",
Expand Down
1 change: 0 additions & 1 deletion code/lib/blocks/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
declare module '@mdx-js/react';
declare module 'global';
declare module 'markdown-to-jsx';
declare module '*.md';
11 changes: 1 addition & 10 deletions code/lib/builder-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@storybook/client-api": "7.0.0-alpha.39",
"@storybook/client-logger": "7.0.0-alpha.39",
"@storybook/core-common": "7.0.0-alpha.39",
"@storybook/mdx1-csf": "0.0.5-canary.13.9ce928a.0",
"@storybook/mdx2-csf": "0.1.0-next.0",
"@storybook/node-logger": "7.0.0-alpha.39",
"@storybook/preview-web": "7.0.0-alpha.39",
"@storybook/source-loader": "7.0.0-alpha.39",
Expand All @@ -36,20 +36,11 @@
"vite": "^3.1.3"
},
"devDependencies": {
"@storybook/mdx2-csf": "^0.0.3",
"@types/express": "^4.17.13",
"@types/node": "^16.0.0",
"typescript": "~4.6.3",
"vite": "^3.1.3"
},
"peerDependencies": {
"@storybook/mdx2-csf": "^0.0.3"
},
"peerDependenciesMeta": {
"@storybook/mdx2-csf": {
"optional": true
}
},
"publishConfig": {
"access": "public"
},
Expand Down
6 changes: 2 additions & 4 deletions code/lib/builder-vite/src/plugins/mdx-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,11 @@ export function mdxPlugin(options: Options): Plugin {
if (!filter(id)) return undefined;

// @ts-expect-error typescript doesn't think compile exists, but it does.
const { compile } = features?.previewMdx2
? await import('@storybook/mdx2-csf')
: await import('@storybook/mdx1-csf');
const { compile } = await import('@storybook/mdx2-csf');

const mdxCode = String(await compile(src, { skipCsf: !isStorybookMdx(id) }));

const modifiedCode = injectRenderer(mdxCode, Boolean(features?.previewMdx2));
const modifiedCode = injectRenderer(mdxCode, true);

// Hooks in recent rollup versions can be functions or objects, and though react hasn't changed, the typescript defs have
const rTransform = reactRefresh?.transform;
Expand Down
3 changes: 1 addition & 2 deletions code/lib/cli/src/generators/baseGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@ export async function baseGenerator(
addonPackages.push('@storybook/addon-interactions', '@storybook/testing-library');
}

const yarn2ExtraPackages =
packageManager.type === 'yarn2' ? ['@storybook/addon-docs', '@mdx-js/[email protected]'] : [];
const yarn2ExtraPackages = packageManager.type === 'yarn2' ? ['@storybook/addon-docs'] : [];

const files = await fse.readdir(process.cwd());

Expand Down
Loading

0 comments on commit a5ff418

Please sign in to comment.