Storybook: addon-storysource not producing useful code examples using recommended stories approach

Created on 16 Sep 2020  路  6Comments  路  Source: storybookjs/storybook

Is your feature request related to a problem? Please describe.

I've been following the tutorials and docs to create many stories using the props driven pattern described there. I have projects in React, Svelte, and Vue, and each have their own storybook with similar configurations.

Using the prescribed patterns in the featured storybook docs/tutorials this is the approach I've used (it's Svelte based, but I've mostly done the same thing for React and Vue with similar results):

import Button from './Button.svelte';

export default {
  title: 'Button',
  component: Button,
  argTypes: {
    label: { control: 'text' },
...and so on
  },
};

const Template = ({ onClick, ...args }) => ({
  Component: Button,
  props: args,
});

export const Primary = Template.bind({});
Primary.args = {
  label: 'Button',
  mode: 'primary',
};
...and so on

When I show code or look at the addon-storysource I see the template code which won't be a helpful dev ux for readers of my design system. Note the examples are really showing the Template intermediary component and are all the same:

image

and same for the addon-storysource tab:

image

Describe the solution you'd like
I'm listing this as a feature as I believe the addon is working as designed. However, I'm confused on the overall result鈥攊f this props driven approach to testing is the recommendation, but the code snippet examples derived are essentially meaningless to the dev users, how am I able to achieve the goal of documenting how to use a component?

As a previous user of react-styleguidist (and other similar tools), getting code snippets was fairly trivial. For styleguidist, it would simply output the code blocks within your markdown giving you some control of what the user would see.

I'm fairly new to storybook but have already written a slew of these stories so I'm hoping I've missed something in the usage somehow.

Describe alternatives you've considered
I've seen some older storiesOf examples that seem to use the template or jsx more closely and wonder if those output better code examples. However, I've written so many of these stories using the docs recommended way that it would be a bear to refactor them all, especially if I'm using an older less idiomatic approach then the props driven newer one that's clearly being featured on the docs and tutorial pages for storybook.

Are you able to assist bring the feature to reality?
no | yes, I can...

Additional context

To be clear, I'd expect the source code examples to show something more meaningful like this I've taken from another similar project I've worked on:

image

docs source has workaround question / support

All 6 comments

Ok, this may turn into a self resolving issue, but I'll keep my notes for others having same quandary :)

I'm starting to learn more digging deeper into the docs section and considering MDX and other alternatives per your writing docs guides:
https://storybook.js.org/docs/react/writing-docs/introduction, and also looking more closely at some of the examples like https://github.com/storybookjs/design-system/blob/master/src/components/Button.stories.js.

So, it looks for React this approach can work (alongside the more modern props approach I showed above):

export const DisabledAll = () => (
  <>
    <Button label="Default Disabled" isDisabled />
    <Button mode="primary" label="Primary Disabled" isDisabled />
    <Button mode="secondary" label="Secondary Disabled" isDisabled />
    <Button mode="secondary" label="Secondary Bordered Disabled" isBordered isDisabled />
  </>
);

This seems to get me the code snippets I'm after more or less (React):

image

So it looks like for React I have a solution.

However, and I could be wrong, but for Vue and Svelte, it seems I need either some sort of special storybook loader that I'm not aware of, or, I have to just create separate example files and then delegate to those.

So here's a new ButtonsDisabled.vue view:


<template>
  <div>
    <AgnosticButton label="Default Disabled" isDisabled />
    <AgnosticButton mode="primary" label="Primary Disabled" isDisabled />
    <AgnosticButton mode="secondary" label="Secondary Disabled" isDisabled />
    <AgnosticButton mode="secondary" label="Secondary Bordered Disabled" isBordered isDisabled />
  </div>
</template>

<script>
import AgnosticButton from "./Button.vue";
export default {
  name: "buttons-disabled",
  components: { AgnosticButton },
};
</script>

And then when I use that in my story like:

import ButtonsDisabled from './ButtonsDisabled.vue';
export const ButtonsDisabledTest = (args) => ({
  title: 'Buttons Disabled',
  components: { ButtonsDisabled },
  template: `<buttons-disabled />`,
});

I get this uninteresting rendering in terms of the code source example:

image

So this last example was Vue, but I have similar results/issues with Svelte. It seems that I have to place the templates / views in another files e.g. view.svelte or view.vue and then delegate from my story, but that results in a not so useful example code snippet.

If you write the code like this:

export const Foo = () => <Button>bar</Button>

The source code should show:

() => <Button>bar</Button>

In 6.0 we've introduced the Args construct, which you're using in the example above.

As the Story function becomes more abstract, it gets further from what you'd want, as you note in the issue.

In React, I've introduced dynamic snippet rendering, so that we render the JSX based on the live values of the args being passed into the function.

So the if { children: "bar" } was passed into this story:

export const Foo = ({ children }) => <Button>{children}</Button>

The source block would display:

<Button>bar</Button>

This was implemented in https://github.com/storybookjs/storybook/pull/11332. There are followup issues for some of the popular frameworks: https://github.com/storybookjs/storybook/issues/11400, https://github.com/storybookjs/storybook/issues/11588, https://github.com/storybookjs/storybook/issues/10617

If you're interested in contributing an implementation for your favorite framework, I'm happy to help guide. Otherwise, you can either use the non-args stories, or you can manually override the source snippet to be anything you want using the docs.source.code story parameter.

Would like to roll as many of these out as I can in 6.1. The current setup leaves a lot to be desired.

Thanks for the timely reply sir! I see addons/docs/src/blocks/SourceContainer.tsx seems to do a lot of the heavy lifting there. In any event, I think this will work itself out in time esp. with these dynamic snippet rendering things you're starting to add!

I'm not sure I can commit to contributing atm but I'll keep it in mind and can empathize that you'd prefer to shepherd collective ownership and contribution from the community!

So I'm confused why React works so well (but Vue and Svelte do not) when I do something like this I get the snippet I'd expect:

export const DisabledAll = () => (
  <>
    <Button label="Default Disabled" isDisabled />
    <Button mode="primary" label="Primary Disabled" isDisabled />
    <Button mode="secondary" label="Secondary Disabled" isDisabled />
    <Button mode="secondary" label="Secondary Bordered Disabled" isBordered isDisabled />
  </>
);

image

image

The above two screen shots are perfect for my use case. So, after refactoring, React is working well for my use case.


Vue

First, I cannot use JSX (or really <template> idiom) within mystory.stories.js so I have to utilize a sub-view (wish I could create a MyVueStories.stories.vue and for Svelte similarly MySvelteStories.stories.svelte) and here's what I've tried for that using the sub-view approach:

<template>
  <div>
    <AgnosticButton label="Default Disabled" isDisabled />
    <AgnosticButton mode="primary" label="Primary Disabled" isDisabled />
    <AgnosticButton mode="secondary" label="Secondary Disabled" isDisabled />
    <AgnosticButton mode="secondary" label="Secondary Bordered Disabled" isBordered isDisabled />
  </div>
</template>

<script>
// ButtonsDisabled.vue
import AgnosticButton from "./Button.vue";
export default {
  name: "buttons-disabled",
  components: { AgnosticButton },
};
</script>

import ButtonsDisabled from './ButtonsDisabled.vue';

export default {
  title: 'Button',
  component: AgnosticButton,
  // etc.
}

export const ButtonsDisabledAll = () => ({
  title: 'Buttons Disabled',
  components: { ButtonsDisabled },
  template: `<buttons-disabled />`,
  parameters: {
    docs: {
      source: {
        code: `
          <div>
            <AgnosticButton label="Default Disabled" isDisabled />
            <AgnosticButton mode="primary" label="Primary Disabled" isDisabled />
            <AgnosticButton mode="secondary" label="Secondary Disabled" isDisabled />
            <AgnosticButton mode="secondary" label="Secondary Bordered Disabled" isBordered isDisabled />
          </div>
        `
      }
    }
  }
});

image

image

Is this the expected output? You can see I've attempted to leverage the docs.source.code mechanism, but I would have thought that would produce output somewhere in the rendered page canvas somehow. Obviously, noob here and I'm not sure how that's supposed to work exactly just referencing https://storybook.js.org/docs/react/writing-docs/doc-blocks#source.

Also, it's not ideal since I have to manually duplicate the code. But I'm just trying things out. I haven't yet started trying to hack on the storybook codebase yet, but was hoping I wouldn't have to do so to achieve code snippets for Vue/Svelte. Lmk if there's anything I'm doing wrong or something else I can try before resorting to working on a patch upstream. Thanks for your guidance 馃檹

Ok, I'm leaving all my user-land mistakes above in case someone else is having same issues :) Here's what fixed the docs.source.code part (my fault for straying from docs in earlier attempts):

export const ButtonsDisabledAll = () => ({
  title: 'Buttons Disabled',
  components: { ButtonsDisabled },
  template: `<buttons-disabled />`,
});
ButtonsDisabledAll.parameters = {
  docs: {
    source: {
      code: `
<div>
  <AgnosticButton label="Default Disabled" isDisabled />
  <AgnosticButton mode="primary" label="Primary Disabled" isDisabled />
  <AgnosticButton mode="secondary" label="Secondary Disabled" isDisabled />
  <AgnosticButton mode="secondary" label="Secondary Bordered Disabled" isBordered isDisabled />
</div>
        `
    }
  },
}

So this works pretty well for snippet in Docs tab:

image

But for the Story tab:

image

I suppose I could just go without using the addon-storysource since the Docs is showing the source there 馃 -- still not ideal to have to manually copy code snippets into docs.source.code as those will likely diverge, but at least it's sort of working.


I also realized that if I comprimise with the docs.source.code idiom, I can also do the props approach with it and it works ok:


export const Primary = Template.bind({});
Primary.args = {
  label: 'Primary',
  mode: 'primary',
};
Primary.parameters = {
  docs: {
    source: {
      code: `<AgnosticButton mode="primary" label="Primary" />`
    }
  },
}

image

In a way, this is closer to being satisfactory then the sub-view approach (if it's the best we can do), since the props and the hard coded docs.source.code snippet are right besides each other (label and mode in this case) so it's a bit more likely that divergent props would be caught be the eye maybe.


I'm still wondering if there's a more ideal approach that will synchronize what's rendered w/source snippets better for Vue and Svelte. Any ideas or recommendations?

yes I would also be really interested in having shown the exact source code of the generated vue-component over the written storysource code. Our developers would prefer to copy the snippet of the component. In the past we used the addon https://github.com/pocka/storybook-addon-vue-info.

Was this page helpful?
0 / 5 - 0 ratings