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: New Markdown block #20796

Merged
merged 13 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
23 changes: 19 additions & 4 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,14 +547,15 @@ This will create a `.babelrc.json` file. This file includes a bunch of babel plu

The `transcludeMarkdown` option in `addon-docs` have been removed, and the automatic handling of `.md` files in Vite projects have also been disabled.

Instead `.md` files can be imported as plain strings by adding the `?raw` suffix to the import. In an MDX file that would look like this:
Instead `.md` files can be imported as plain strings by adding the `?raw` suffix to the import, and then passed to the new `Markdown` block. In an MDX file that would look like this:

```
import { Markdown } from '@storybook/blocks';
import ReadMe from './README.md?raw';

...

{ReadMe}
<Markdown>{ReadMe}</Markdown>

```

Expand Down Expand Up @@ -858,6 +859,20 @@ The props have been simplified and the block now only accepts an `of` prop, whic

`parameters.notes` and `parameters.info` have been deprecated as a way to specify descriptions. Instead use JSDoc comments above the default export or story export, or use `parameters.docs.description.story | component` directly. See TDB DOCS LINK for a deeper explanation on how to write descriptions.

If you were previously using the `Description` block to render plain markdown in your docs, that behavior can now be achieved with the new `Markdown` block instead like this:

```
import { Markdown } from '@storybook/blocks';
import ReadMe from './README.md?raw';

...

<Markdown>{ReadMe}</Markdown>

```

Notice the `?raw` suffix in the markdown import is needed for this to work.

##### Story block

To reference a story in a MDX file, you should reference it with `of`:
Expand Down Expand Up @@ -1017,8 +1032,8 @@ Then enable the `legacyMdx1` feature flag in your `.storybook/main.js` file:
export default {
features: {
legacyMdx1: true,
}
}
},
};
```

NOTE: This only affects `.(stories|story).mdx` files. Notably, if you want to use Storybook 7's "pure" `.mdx` format, you'll need to use MDX2 for that.
Expand Down
2 changes: 1 addition & 1 deletion code/ui/blocks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"color-convert": "^2.0.1",
"dequal": "^2.0.2",
"lodash": "^4.17.21",
"markdown-to-jsx": "^7.1.3",
"markdown-to-jsx": "^7.1.8",
"memoizerific": "^1.11.3",
"polished": "^4.2.2",
"react-colorful": "^5.1.2",
Expand Down
4 changes: 2 additions & 2 deletions code/ui/blocks/src/blocks/Description.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import type { FC } from 'react';
import React, { useContext } from 'react';
import { str } from '@storybook/docs-tools';
import { deprecate } from '@storybook/client-logger';
import { Description } from '../components';

import type { DocsContextProps } from './DocsContext';
import { DocsContext } from './DocsContext';
import type { Component } from './types';
import type { Of } from './useOf';
import { useOf } from './useOf';
import { Markdown } from './Markdown';

export enum DescriptionType {
INFO = 'info',
Expand Down Expand Up @@ -151,7 +151,7 @@ const DescriptionContainer: FC<DescriptionProps> = (props) => {
`The 'children' prop on the Description block is deprecated. See ${DEPRECATION_MIGRATION_LINK}`
);
}
return markdown ? <Description markdown={markdown} /> : null;
return markdown ? <Markdown>{markdown}</Markdown> : null;
};

export { DescriptionContainer as Description };
75 changes: 75 additions & 0 deletions code/ui/blocks/src/blocks/Markdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import dedent from 'ts-dedent';
import { Markdown as MarkdownComponent } from './Markdown';
// eslint-disable-next-line import/no-unresolved
import mdContent from '../examples/Markdown-content.md?raw';

export default {
component: MarkdownComponent,
};

export const Markdown = {
args: {
children: dedent`
# My Example Markdown

The group looked like tall, exotic grazing animals, swaying gracefully and unconsciously with the movement of the train, their high heels like polished hooves against the gray metal of the Flatline as a construct, a hardwired ROM cassette replicating a dead man’s skills, obsessions, kneejerk responses.

![An image](https://storybook.js.org/images/placeholders/350x150.png)

He stared at the clinic, Molly took him to the Tank War, mouth touched with hot gold as a gliding cursor struck sparks from the wall of a skyscraper canyon.

Paragraph with an \`inline code\` block.

\`\`\`tsx
// TypeScript React code block
export const MyStory = () => {
return <Button>Click me</Button>;
};
\`\`\`

\`\`\`
code block with with no language
const a = fn({
b: 2,
});
\`\`\`

<h3>Native h3 element</h3>

# [Link](https://storybook.js.org/) in heading
## [Link](https://storybook.js.org/) in heading
### [Link](https://storybook.js.org/) in heading
#### [Link](https://storybook.js.org/) in heading
##### [Link](https://storybook.js.org/) in heading
###### [Link](https://storybook.js.org/) in heading

He stared at the clinic, [Molly](https://storybook.js.org/) took him to the *[Tank War](https://storybook.js.org/)*, mouth touched with hot gold as a gliding cursor struck sparks from the wall of a **[skyscraper](https://storybook.js.org/)** canyon.

{ brackets, valid MD but invalid MDX - works here }

<Looks like a JSX tag/>
<!-- above is valid MD but invalid in markdown-to-jsx, so it will not be rendered -->

\`<Looks like a JSX tag />\`

The above is only visible because it is wrapped in backticks
`,
},
};

/**
* The Markdown component won't know the difference between getting a raw string
* and something imported from a .md file.
* So this story doesn't actually test the component, but rather the import
* at the top of the CSF file
*/
export const ImportedMDFile = {
name: 'Imported .md file',
args: { children: mdContent },
};

export const Text = {
args: {
children: `That was Wintermute, manipulating the lock the way it had manipulated the drone micro and the amplified breathing of the room where Case waited. The semiotics of the bright void beyond the chain link. The tug Marcus Garvey, a steel drum nine meters long and two in diameter, creaked and shuddered as Maelcum punched for a California gambling cartel, then as a paid killer in the dark, curled in his capsule in some coffin hotel, his hands clawed into the nearest door and watched the other passengers as he rode. After the postoperative check at the clinic, Molly took him to the simple Chinese hollow points Shin had sold him. Still it was a handgun and nine rounds of ammunition, and as he made his way down Shiga from the missionaries, the train reached Case’s station. Now this quiet courtyard, Sunday afternoon, this girl with a random collection of European furniture, as though Deane had once intended to use the place as his home. Case felt the edge of the Flatline as a construct, a hardwired ROM cassette replicating a dead man’s skills, obsessions, kneejerk responses. They were dropping, losing altitude in a canyon of rainbow foliage, a lurid communal mural that completely covered the hull of the console in faded pinks and yellows.`,
},
};
50 changes: 50 additions & 0 deletions code/ui/blocks/src/blocks/Markdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable react/destructuring-assignment */
import React from 'react';
import PureMarkdown from 'markdown-to-jsx';
import dedent from 'ts-dedent';
import { AnchorMdx, CodeOrSourceMdx, HeadersMdx } from './mdx';

// mirror props from markdown-to-jsx. From https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase#wrappingmirroring-a-component
type MarkdownProps = typeof PureMarkdown extends React.ComponentType<infer Props> ? Props : never;

export const Markdown = (props: MarkdownProps) => {
if (!props.children) {
return null;
}
if (typeof props.children !== 'string') {
throw new Error(
dedent`The Markdown block only accepts children as a single string, but children were of type: '${typeof props.children}'
This is often caused by not wrapping the child in a template string.

This is invalid:
<Markdown>
# Some heading
A paragraph
</Markdown>

Instead do:
<Markdown>
{\`
# Some heading
A paragraph
\`}
</Markdown>
`
);
}
return (
<PureMarkdown
{...props}
options={{
forceBlock: true,
overrides: {
code: CodeOrSourceMdx,
a: AnchorMdx,
...HeadersMdx,
...props?.options?.overrides,
},
...props?.options,
}}
/>
);
};
1 change: 1 addition & 0 deletions code/ui/blocks/src/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export * from './DocsStory';
export * from './external/ExternalDocs';
export * from './external/ExternalDocsContainer';
export * from './Heading';
export * from './Markdown';
export * from './Meta';
export * from './Primary';
// eslint-disable-next-line import/export
Expand Down
70 changes: 0 additions & 70 deletions code/ui/blocks/src/components/Description.stories.tsx

This file was deleted.

23 changes: 0 additions & 23 deletions code/ui/blocks/src/components/Description.tsx

This file was deleted.

13 changes: 7 additions & 6 deletions code/ui/blocks/src/components/DocsPage.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import type { ComponentProps } from 'react';
import React from 'react';
import { Global, css } from '@storybook/theming';
import { Source, ArgsTable, Description } from '.';
import { Source, ArgsTable } from '.';
import { Title, Subtitle, DocsPageWrapper } from './DocsPage';
import { Markdown as MarkdownComponent } from '../blocks/Markdown';
import * as Preview from './Preview.stories';
import * as argsTable from './ArgsTable/ArgsTable.stories';
import * as source from './Source.stories';
import * as description from './Description.stories';
import * as markdown from '../blocks/Markdown.stories';
import { Unstyled } from '../blocks/Unstyled';

export default {
Expand Down Expand Up @@ -40,7 +41,7 @@ export const Loading = () => (
<Subtitle>
What the DocsPage looks like. Meant to be QAed in Canvas tab not in Docs tab.
</Subtitle>
<Description {...description.Text.args} />
<MarkdownComponent {...markdown.Text.args} />
<Preview.Loading />
<ArgsTable {...(argsTable.Loading.args as ComponentProps<typeof ArgsTable>)} />
<Source {...source.Loading.args} />
Expand All @@ -53,7 +54,7 @@ export const WithSubtitle = () => (
<Subtitle>
What the DocsPage looks like. Meant to be QAed in Canvas tab not in Docs tab.
</Subtitle>
<Description {...description.Text.args} />
<MarkdownComponent {...markdown.Text.args} />
<Preview.Single />
<ArgsTable {...argsTable.Normal.args} />
<Source {...source.JSX.args} />
Expand All @@ -72,7 +73,7 @@ export const NoText = () => (
export const Text = () => (
<DocsPageWrapper>
<Title>Sensorium</Title>
<Description {...description.Text.args} />
<MarkdownComponent {...markdown.Text.args} />
<Preview.Single />
<ArgsTable {...argsTable.Normal.args} />
<Source {...source.JSX.args} />
Expand All @@ -82,7 +83,7 @@ export const Text = () => (
export const Markdown = () => (
<DocsPageWrapper>
<Title>markdown</Title>
<Description {...description.Markdown.args} />
<MarkdownComponent {...markdown.Markdown.args} />
<Preview.Single />
<ArgsTable {...argsTable.Normal.args} />
<Source {...source.JSX.args} />
Expand Down
1 change: 0 additions & 1 deletion code/ui/blocks/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export * from './Source';
export * from './EmptyBlock';
export * from './Description';
export * from './DocsPage';
// eslint-disable-next-line import/no-cycle
export * from './Preview';
Expand Down
14 changes: 14 additions & 0 deletions code/ui/blocks/src/examples/Markdown-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This is an `.md` file

it has been imported using `import content from './Markdown-content.md?raw'`

Notice the `?raw` at the end above, it is necessary to work.

A full example:

```md
import { Markdown } from '@storybook/blocks';
import content from './Markdown-content.md?raw';

<Markdown>{content}</Markdown>
```
1 change: 0 additions & 1 deletion code/ui/blocks/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint-disable no-underscore-dangle */
/* eslint-disable @typescript-eslint/naming-convention */
declare module 'markdown-to-jsx';
declare module '*.md';

declare var __DOCS_CONTEXT__: any;
Expand Down
1 change: 0 additions & 1 deletion code/ui/components/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
declare module 'markdown-to-jsx';
declare module '*.md';
declare module '*.mdx';
Loading