Storybook: Add support for Vue SFC syntax to stories

Created on 5 Feb 2020  ·  14Comments  ·  Source: storybookjs/storybook

Is your feature request related to a problem? Please describe.
When writing a story for Vue components inside mdx files, it differs from the way one would write the code inside a .vue file.

Also when reading the story source inside storybook, the code differs from the code one would normally put inside a .vue file.

So there is this mental switch developers have to do, when using storybook as their component documentation. This is especially troublesome for new developers, which in many teams are the main target group of the documentation.

Describe the solution you'd like
In addition to writing a Vue story like this:

<Story name='basic' height='400px'>{{
  components: { InfoButton },
  template: '<info-button label="I\'m a button!"/>',
}}</Story>

It would be nice to add support for stories in this format:

<Story name='basic' height='400px'>
<template>
  <info-button label="I\'m a button!"/>
</template>
<script>
  export default {
    components: { InfoButton },
  }
</script>
</Story>

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

docs vue feature request in progress

Most helpful comment

@graup Initially I was aiming for a solution similar to the one you proposed. But as @Aaron-Pool pointed out the syntax highlighting with this approach was not great. Having the fenced code block with vue around it lets markdown syntax highlighter recognize it as Vue SFC code.

All 14 comments

@visualjerk this looks awesome!

Related: https://github.com/storybookjs/storybook/issues/7729

@Aaron-Pool has thought about this problem quite a bit and might be able to give pointers.

Yeah, so there's two major parts of trying to get this feature implemented

1) making it work
2) making the developer experience not feel horrible

As for 1, I think we could use Babel macros to run just the contents of a story block (with a comment to trigger the macro, or something similar) through the Vue sfc compiler and Vue Babel preset.

2 is much trickier. And would be editor dependent. But for vscode, at least, I'm pretty confident it would be possible to write an extension that could detect .vue as an "embedded language" within story blocks marked with a Vue indicator. I dunno how well code complete and intellisense would work, but the syntax highlighting would probably be fine, at least.

Thanks for the insights @Aaron-Pool

Regarding 1: Do you already have plans to work on that? If not, I would be glad to help implementing it.

Regarding 2: At least for syntax highlighting in VS Code the silvenon.mdx extension already does a pretty good job with stories written in this syntax.

@visualjerk I've had "plans" to work on it for a while now, but my day job has been eating up most of coding creative energy as of late. I'd be glad to offer guidance and help with any technical hurdles if you started a PR though 👌

And glad to know the current MDX plugin does a decent job already, I though I remembered it not knowing how to handle a non-root script tag, but I'm glad to be wrong 🤙

It sounds a great idea.

@visualjerk
But if you want a awesome developer experience maybe the best is to use SFC (single file component) and the CSF (component story format).

// ButtonNormal.story.vue
<template>
    <Button>Normal Button</Button>
</template>

<script>
import Button from '../src/components/Button.vue';

export default {
    name: 'ButtonNormal',
    components: { Button },
};
</script>
// Button.stories.ts
import ButtonNormalStory from './ButtonNormal.story.vue';

export default {
    title: 'Button',
};

export const normalButton = () => ButtonNormalStory;

See this article for more details
https://dev.to/josephuspaye/using-storybook-with-vue-single-file-components-2od

Note: The cons is the story source is not pertinent (displayed via story-source / docs addon). The article explains how to load the SFC vue story but i have tried without success.

@rdhainaut will try to get an addon-docs Source fix for this scenario in 6.0 .. 🤞

@rdhainaut thanks for pointing out that solution. Maybe we will give it a try and see how it works with multiple stories in one docs file.

@Aaron-Pool After working on the feature request for a while I can give a quick update:

1. Implementation

The current implementation uses babel parser, which unfortunately does not support custom plugins: https://babeljs.io/docs/en/babel-parser#will-the-babel-parser-support-a-plugin-system

So it looks like we need a solution that handles SFC stories before babel parser does.

A possible approach would be to detect SFC stories and transform them to JSX stories. Afterwards babel can handle them just fine.

Additionally we would have to keep the original SFC story code and put it into the mdxSource later on, so docs can show it in the code preview.

2. Developer experience

I was wrong 😄 While the mdx extension mentioned above works fine for simple code snippets, it breaks with things like :rounded="true inside the template tag.

However we could provide a different syntax for SFC stories that works like a charm:

mdx <Story name="to storybook" height="300px"> ```vue <template> <button>click me {{ name }}</button> </template> <script> export default { data() { return { name: 'foo' } } } </script> ``` </Story>

If anybody here hasn't seen it, I recently made source snippet customizable, which could be used to improve the Source block for these SFC stories (which are awesome!!!). Here's the PR: https://github.com/storybookjs/storybook/pull/10089

@visualjerk Is there any standard/precedent for this ```vue ... string? It looks a bit odd to me. It might make syntax highlighting difficult? I wonder if we should coordinate with @mdx-js/vue-loader to add this feature there first.

@graup thanks for the reference. I think https://github.com/mdx-js/mdx/tree/master/packages/vue-loader is the converse of what we want. It takes MDX and loads it into a Vue component. We want to load MDX into a React component since Storybook Docs is implemented in React. I'm also not sure whether @visualjerk 's approach is the best one, but it seems very reasonable at first glance. Definitely open for discussion.

@shilman you're right, I confused the purpose of @mdx-js/vue-loader.

Here's another idea instead of <Story> ```vue: how about something like <Story lang="vue">? That would feel pretty familiar to vue developers and be just as easily regex-able.

@graup I think the reason he went with the notated template string was because this gives us a lot of the DX provided by the markdown syntax highlighter for free (in most code editors, at least) 🤷‍♂️

If we want a different syntax, and a decent developer experience, we're going to need to work the MDX syntax highlighting extension and add some additional functionality to detect story block lang attribute.

@graup Initially I was aiming for a solution similar to the one you proposed. But as @Aaron-Pool pointed out the syntax highlighting with this approach was not great. Having the fenced code block with vue around it lets markdown syntax highlighter recognize it as Vue SFC code.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alexanbj picture alexanbj  ·  3Comments

MrOrz picture MrOrz  ·  3Comments

sakulstra picture sakulstra  ·  3Comments

rpersaud picture rpersaud  ·  3Comments

zvictor picture zvictor  ·  3Comments