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

Addon-docs: "Native"/natural MDX syntax #7984

Closed
1 of 6 tasks
shilman opened this issue Sep 4, 2019 · 13 comments
Closed
1 of 6 tasks

Addon-docs: "Native"/natural MDX syntax #7984

shilman opened this issue Sep 4, 2019 · 13 comments

Comments

@shilman
Copy link
Member

shilman commented Sep 4, 2019

Umbrella issue to extend "native" MDX from React to other frameworks

Problem

Currently in @storybook/react you can write JSX directly in your MDX file:

<Story name='foo'><div className='bar'>baz</div></Story>

For @storybook/html you'd probably like to write the following, but it won't work:

<Story name='foo'><div class='bar'>baz</div></Story>

Instead you'd have to write:

<Story name='foo'>{`<div class='bar'>baz</div>`}</Story>

Here's what's going on:

  1. The MDX is getting parsed by the @mdx-js/mdx parser
  2. The resulting JSX is getting processed by our mdx-compiler-plugin
  3. The output of the plug in CSF
  4. CSF is getting loaded into the storiesOf API

The contents of the <Story> element is isomorphic to the return value of the CSF function, which is equal to the storiesOf storyFn.

Solution

For an alternative syntax to work, there are a bunch of things that need to happen:

  1. We need to determine the desired syntax
  2. Can it be parsed by the MDX parser?
  3. Can it be transformed into the format required by the storiesOf API (the return value of storyFn for that particular framework)
  4. Is that the currently storiesOf format we want to support in the future? I.e. if CSF is meant to become an open standard, we probably might to give that more thought to that. E.g. Svelte: Svelte syntax Component Story Format #7682 rethinks that for Svelte

Frameworks

We can do this framework by framework. Here are the ones that are on my radar right now. Can add more later.

  • React
  • Vue
  • Angular
  • HTML
  • Svelte
  • StencilJS
@Aaron-Pool
Copy link
Contributor

Have you considered writing a babel-plugin-macro for this? They can be used as a JSX element. Then every framework could just implement their own <Story> block.

Honestly the macro approach for a lot of the work that the compiler plug-in is doing would probably need to a nicer, more modular approach all-around for augmenting mdx to suit the needs of storybook. And we wouldn't have to deal with a bunch Babel configuration.

Also Kent Dodds is brilliant and I never miss a chance to use his work :)

@shilman
Copy link
Member Author

shilman commented Sep 4, 2019

@Aaron-Pool I'm not familiar with it. Sounds very cool though! Can you sketch out what that solution might look like?

@atanasster
Copy link
Member

atanasster commented Sep 4, 2019

Sorry, I am bit new to the source code and it seems I might be missing something especially wrt html.
At first glance - react/react-dom is still linked into the storybook app/ui - so even in an html app, react is still present and we can use it to render <h1/> type syntax? Also the mdx parser/loader does support <h1/> type syntax and will not spew out an error (the csf stories are different, since they don't go through the mdx parser/compiler). And we can use the react renderer?

Anyway, here is what i tried for html:

changed the code as follow:

  } else {
    console.log(element);
    ReactDOM.render(element, rootElement);
    return ; //shortcircuit the error
    ...
    showError({

and changed the 'addon-doc.stories.mdx' example from 'examples/html-kitchen-sink':

<Story name="heading" height="100px">
  <h1>Hello World</h1>
</Story>

and the output is:

screenshot818

@atanasster
Copy link
Member

tried also the above example:
<Story name='foo'><div class='bar'>baz</div></Story>

it works, except the html prop names need to follow the react syntax, so this works instead:
<Story name='foo'><div className='bar'>baz</div></Story>
respectively:
<Story name='foo'><div className='bar' style={{ backgroundColor: 'red'}}>baz</div></Story>

@shilman
Copy link
Member Author

shilman commented Sep 4, 2019

@atanasster Are you running that in @storybook/html?

When you write <div className='bar'>baz</div>, this is getting compiled down to something like::

React.createElement('div', { className: 'bar', children: [ .... ] })

The point of @storybook/html is that your story function can return a pure HTML string (or as an HTML webcomponent, etc.?) and it will get rendered as HTML in the browser.

@storybook/html should have no dependency on React in the preview (the canvas iframe) because the user's app should not need to depend on React. All the storybook frameworks currently have a dep on react since Storybook's UI is written in react (but this could change in the future when we move over to a decorator-based approach: #3889).

Addon-docs muddies the issue since we are rendering MDX markdown and Doc Blocks in React (and it happens to be in in the preview as an implementation detail). But conceptually, the user's stories should not need to know about React at all unless they are using @storybook/react or @storybook/react-native.

It's possible that JSX syntax is a pure superset of HTML and we can fake out the MDX parser to convert that JSX back into a string. But I wouldn't count on this working in all cases unless it's explicitly part of the spec.

@atanasster
Copy link
Member

atanasster commented Sep 4, 2019

Are you running that in @storybook/html - I have no idea, sorry :)

For the rest - I am getting there, correct me if I am wrong:

  1. We should not use the react renderer for anything inside the iframe (dep on react will be removed)
  2. We need to convert mdx to html strings at compile time
  3. How are we to determine if the conversion is necessary (possibly mixed files in a project html/react)?

@shilman
Copy link
Member Author

shilman commented Sep 4, 2019

Until we've fully switched over #3889 (much later ... hopefully SB7.0?) each framework has its own app. In the monorepo those are app/react, app/html, etc. Each app has its own start-storybook and build-storybook binaries, most of which is defined in @storybook/core, and a unique render function that is responsible for rendering the results of the storyFn (specific to that framework) into DOM nodes. So if you're using Storybook, you should see @storybook/[framework] in your devDependencies (and those frameworks are mutually exclusive -- not sure what happens when you try to install @storybook/react and @storybook/html at the same time, for example. probably bad things.)

There are a couple different ways to know what framework you're in. You probably have a line in your presets.js file that looks like:

module.exports = ['@storybook/addon-docs/react/preset'];

If you're using @storybook/vue you should replace react with vue. I had to do this for reasons that aren't worth going into (TLDR: there is no global addParameters call for the preset ... I need to get it from the specific package). But it's not a bad thing since it's future-proof. It means that as long as you're using Vue, we can modify the preset to whatever tricky thing we need to do for Vue in the future.

Another way to tell what framework you're in is to look at the framework parameter, which gets automatically set by @storybook/[framework].

This has all come together a bit organically with SB Docs, and I expect we'll want to rethink it as part of #3889 (probably as a breaking change in 6.0 cc @Hypnosphi )

@Aaron-Pool
Copy link
Contributor

@shilman yeah, I'll try to do a little summary of what a babel-plugin-macro approach might look like when I get a free minute.

@shilman
Copy link
Member Author

shilman commented Sep 4, 2019

Here's a comment from a slack user, kind of contradicting what I said above:

On a random side note, interestingly with this mdx format I've been able to drop storybook/html and use storybook/react for my web components, the behavior of this works much nicer as the JSX support makes it easy to render the web components, can't see any downsides thus far.

Here's a snippet:

import '.';
import '../icon';
import { Meta, Story, Preview, html, text, boolean } from '[redacted]';
import ReadMe from './README.md';

<Meta title="UI Kit|Accordion" />

<!-- Workaround until transclusion of md files is supported outright -->
<ReadMe.parameters.docs.page />

## Stories

<Preview>
    <Story name="default">
        <magic-ui-accordion>
            <b slot="toggle">{text('Toggle Text', 'Default Toggle Text')}</b>
            <ul slot="content">
                <p>This is some content to display!</p>
                <p>It's toggled every time the page is toggled</p>
            </ul>
        </magic-ui-accordion>
    </Story>
</Preview>

@atanasster
Copy link
Member

@shilman then what about we fix the html app using ReactDOM.render(element, rootElement); as above until we would remove the react dependency?
In any case, the alternative is now throwing an error?

@Aaron-Pool
Copy link
Contributor

@shilman I believe that's what they call it when a bug becomes a feature 😄

@shilman shilman added the mdx label Oct 10, 2019
@shilman shilman modified the milestones: 5.3.0, 5.4.0 Nov 7, 2019
@shilman shilman modified the milestones: 5.4.0, 6.0.0 Dec 12, 2019
@0gust1
Copy link

0gust1 commented Dec 20, 2019

For information, on Svelte side, the equivalent of MDX is https://github.com/pngwn/MDsveX
I did some extensive tests, it's handled pretty well by the Svelte compiler (as a preprocess step that is feeded on the compiler). On Storybook side, I still have to catch up on the corresponding MRs.

@shilman shilman removed this from the 6.0.0 milestone Dec 23, 2019
@shilman
Copy link
Member Author

shilman commented Jun 14, 2023

In 7.0 we've deprecated defining stories in MDX and have introduced a better syntax for importing CSF stories into MDX. More information https://storybook.js.org/blog/storybook-7-docs/

@shilman shilman closed this as completed Jun 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants