storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
84.1k stars 9.25k forks source link

addon-docs with regular markdown #9289

Closed LarsDenBakker closed 4 years ago

LarsDenBakker commented 4 years ago

Describe the solution you'd like .mdx is a pretty cool format, but it may not play well with every type of framework and requires a transformation step. It would be cool if the docs addon could work with regular markdown and html as well.

Because markdown can render HTML elements, you can use that to embed stories and cover at least the basic use cases.

It could look something like this:

example.stories.md ```md # Title ## Subtitle 1 Paragraph 1 ## Subtitle Paragraph 2 ```
example.stories.js ```js import React from 'react'; import { action } from '@storybook/addon-actions'; import { fromMarkdown } from '@storybook/addon-docs'; import { Button } from '@storybook/react/demo'; export default { title: 'Button', parameters: { component: Button, docs: { page: fromMarkdown('./example.stories.md'), } }, }; export const Story1 = () => ; Story1.story = { name: 'with text' }; export const Story2 = () => ( ); Story2.story = { name: 'with some emoji' }; ```

You could also write both in one file, using a markdown template literal:

markdown template literal ```js import React from 'react'; import { action } from '@storybook/addon-actions'; import { md } from '@storybook/addon-docs'; import { Button } from '@storybook/react/demo'; const page = md` # Title ## Subtitle 1 Paragraph 1 ## Subtitle Paragraph 2 `; export default { title: 'Button', parameters: { component: Button, docs: { page } }, }; export const Story1 = () => ; Story1.story = { name: 'with text' }; export const Story2 = () => ( ); Story2.story = { name: 'with some emoji' }; ```

The docs page wouldn't need to load react in the preview this way, keeping the separation between the manager and the preview clearer.

In a followup step the doc blocks could be exposed as well, thought that will require loading react.

Describe alternatives you've considered This could be a separate addon entirely, but I think it's best to work from one core docs addon and have mdx and md as ways to write docs.

Are you able to assist bring the feature to reality? Yes

shilman commented 4 years ago

How would this integrate with the existing package?

LarsDenBakker commented 4 years ago

I can come up with a more concrete proposal if there is interest in this.

Ideally there would be a 'core' docs mode, which can accept different kind of renderers. MDX and MD being two formats, but plain HTML docs would be useful too for server rendered frameworks as they could just serve out of the docs directly.

shilman commented 4 years ago

TBH I'm not entirely sure.

Here's how I see it:

  1. At the base, Storybook core supports a "docs" viewMode, which renders parameters.docs.container/page in a tab, with a little bit of weird magic about iframes.
  2. Above that, we have CSF, which fills in those parameters via the Storybook API.
  3. Above that, we have MDX, which compiles to CSF.

Because of this architecture, it shouldn't be hard to replace MDX with HTML, Github-flavored Markdown (cc @BPScott), or any other authoring layer.

And since Storybook takes a "more the merrier" approach, I'm definitely in favor of other options.

However, I don't want to add complexity to docs, and I also don't want to confuse users.

Therefore, I'd be open to:

That's my initial reaction -- I'm also open to other ideas! What do you think?

LarsDenBakker commented 4 years ago

Ah I see, I hadn't dug deep enough to see the layering. So @storybook/addon-docs is basically the third MDX layer? Then indeed a separate package seems to be the right way to go for this.

shilman commented 4 years ago

Yeah, more or less. @storybook/addon-docs has got:

We might find that there are common pieces that we want to share after you build a prototype.

One other relevant bit: the doc blocks are implemented in React, but we've talked about some day re-implementing them in some other easily embeddable, framework-independent approach. πŸ˜‰

BPScott commented 4 years ago

(cc @BPScott)

Hi!

Context for Lars: I expect I'm being mentioned as I've got some experience with writing docs in md files and importing them into storybook instead of writing CSF *.stories.js files in JS.

CSF is the common base layer that binds this all together. Writing documentation in new formats (html, markdown etc) is a case of writing a webpack transformer that will take that format (e.g a markdown file) and transform it into CSF. This is basically what addon-docs does under the hood (or so goes my understanding).

I figure an example might be helpful:

Over in https://github.com/shopify/polaris-react I've got our storybook setup so that we write markdown README files for our components, which we then import into storybook. Our webpack config says to run these markdown files through our "polaris-readme-loader". The polaris-readme-loader is responsible for taking that markdown and transforming it into a JS file written in the CSF style (with a default export for metadata and each export is a story); it extracts out the markdown fenced code blocks and creates a story export for each of them. I've not yet got this setup so it generates docs pages for my components, but it would be a case of adding some extra fields onto the default export that gets generated. You might note that this setup is veeery similar to the manual configuration of addon-docs.

daKmoR commented 4 years ago

one possible format could be a special js run markdown block πŸ‘

```js run
import { html, withKnobs, withWebComponentsKnobs } from '...';

export default {
  title: 'Decorators/WithWebComponentKnobs',
  decorators: [withKnobs, withWebComponentsKnobs],
  parameters: { component: 'demo-wc-card', options: { selectedPanel: 'storybookjs/knobs/panel' } }
}
```

# My first story

That is one hell of a preview :+1: 

<docs-story name="Provide own light dom">

```js run
export const ProvideOwnLightDom = () => html`
  <demo-wc-card></demo-wc-card>
`;
```
</docs-story>

Pros:

Cons:


output of above code πŸ‘‡


import { html, withKnobs, withWebComponentsKnobs } from '...';

export default {
  title: 'Decorators/WithWebComponentKnobs',
  decorators: [withKnobs, withWebComponentsKnobs],
  parameters: { component: 'demo-wc-card', options: { selectedPanel: 'storybookjs/knobs/panel' } }
}

My first story

That is one hell of a preview :+1:

```js run export const ProvideOwnLightDom = () => html` `; ```
BPScott commented 4 years ago

The metadata at the top - imports and the export default config could be solved with yaml frontmatter, and the story name could be attached as part of the meta of the component block. That would remove the need for any special markdown parsing. You'd need to read front-matter and extract the contents of the fenced code blocks whose meta begins with story: and convert them into a story but that's you can get all that using graymatter for the frontmatter parsing and remark for reading the md AST.

imports: |
  import {html, withKnobs, withWebComponentsKnobs} from '...';
story: |
  {
    title: 'Decorators/WithWebComponentKnobs',
    decorators: [withKnobs, withWebComponentsKnobs],
    parameters: { component: 'demo-wc-card', options: { selectedPanel: 'storybookjs/knobs/panel' } }
  }
---

# My first story

That is one hell of a preview :+1: 

```js story: Provide own light dom
  <demo-wc-card></demo-wc-card>
LarsDenBakker commented 4 years ago

Id like to avoid creating too much meta syntax, and extra "things to learn".

Using frontmatter for things like title can be interesting, but putting code in there I think is not a good idea.

Im not certain either about custom syntax in the margins of the fenced codeblocks. I dont think there any real standard syntax there, so we dont know what conflicts it might cause. The markdown parsers ive tried so far remove it from their output so that would require customizing the parsing logic.

Using a <docs-story> element as a marker feels right, and is similar to the way mdx docs work as well. Markdown can contain html, and if we need to attach behavior to the element we can make it a custom element (https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements). That would also be how we can support doc blocks in regular markdown, which is a powerful feature of docs mode.

stale[bot] commented 4 years ago

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

LarsDenBakker commented 4 years ago

We will have a first version to show soon :)

nicolashemonic commented 4 years ago

Hi @LarsDenBakker I actually migrate a full Design System to StoryBook and I'm very interested by your proposition. I don't like the MDX format which is another custom syntax while the couple JS+MD(+HTML) is more relevant for learning and migrating sources to another tool in the future if needed. In the meantime I will create documentation using Addon Notes and Markdown. Thank you!

stale[bot] commented 4 years ago

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

stale[bot] commented 4 years ago

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

daKmoR commented 4 years ago

For everyone who is interested, we finally made it happen πŸ’ͺ Took a little longer than expected πŸ˜… the devil lies in the details πŸ™ˆ

I works by wrapping js that should be executed and js that should be a story in special code fence blocks like so:

```js script
import './demo-wc-card.js';
import { html } from 'lit-html';

This is my component

export const demo = () => html`
  <demo-wc-card header="HEADER"></demo-wc-card>
`;


As all the "logic" is in markdown this means we can map it to different environments.

- Works locally with `es-dev-server`
- Works with storybook
- Works online on webcomponents.dev
- Works on github (via a chrome extension)

You can get a really good overview of all the ideas/features that went into this by looking at the twitter thread.
https://twitter.com/OpenWc/status/1250411118218153984?s=20

If you are interested in all the nitty-gritty details then be sure to check out the announcement blog post
https://dev.to/open-wc/introducing-mdjs-interactive-demos-everywhere-16dh

Note: This is powered by `storybook-addon-markdown-docs` which currently only works with `storybook-prebuilt`. Our use-case was buildless dev and production build via rollup. I am however sure this could be with some effort ported to the official storybook webpack version.