Storybook: Docs page doesn't show code preview when default export is a function call

Created on 30 Jul 2020  路  6Comments  路  Source: storybookjs/storybook

Describe the bug

I have been writing stories using CSF, and the auto generated docs show the source code of the stories, as expected.

I'm using Storybook to build a sort of living design reference, so I wanted to add a status badge to each docspage indicating the readiness of each component. After much searching, I settled on the best way to achieve this being configuring parameters.docs.page and adding the badge component at the top. To save having to write this in every file, I made a helper function:

export const ComponentStory = (options: Options) => ({
  title: options.title,
  component: options.component,
  parameters: {
    docs: {
      page: () => (
        <>
          <StatusBadge status={options.componentStatus} />
          <DocBlocks.Title />
          <DocBlocks.Subtitle />
          <DocBlocks.Description />
          <DocBlocks.Primary />
          <DocBlocks.Props />
          <DocBlocks.Stories />
        </>
      ),
    },
  },
})

which is used like this:

export default ComponentStory({
  title: "Components/Brand/CircleIcon",
  component: CircleIcon,
  componentStatus: "review",
});

However, this causes the story previews to show "no code available".

image

To debug a bit I tried writing it without the helper function:

export default {
  title: "Components/Brand/CircleIcon",
  component: CircleIcon,
  parameters: {
    docs: {
      page: () => (
        <>
          <StatusBadge status="review" />
          <DocBlocks.Title />
          <DocBlocks.Subtitle />
          <DocBlocks.Description />
          <DocBlocks.Primary />
          <DocBlocks.Props />
          <DocBlocks.Stories />
        </>
      ),
    },
  },
};

The code preview now works as expected.

image

I suspect this is because something in the chain doesn't recognise the function call version as being CSF.

To Reproduce
Steps to reproduce the behavior:

  1. use a function to generate the default export object for a CSF story

Expected behavior
The code preview on the generated docs page shows the source code for the story.

Screenshots/ Code
See above

System:
Please paste the results of npx -p @storybook/cli@next sb info here.

Environment Info:

  System:
    OS: Linux 4.19 Ubuntu 18.04.2 LTS (Bionic Beaver)
    CPU: (4) x64 Intel(R) Core(TM) i5-4570 CPU @ 3.20GHz
  Binaries:
    Node: 12.16.3 - ~/.nodenv/versions/12.16.3/bin/node
    Yarn: 1.22.0 - /mnt/c/Program Files (x86)/Yarn/bin/yarn
    npm: 6.14.4 - ~/.nodenv/versions/12.16.3/bin/npm

Additional context

If there's an alternative more standard way to achieve what I want to do then I'm all ears. It seems a bit of a hassle to override the layout, when what I want feels more like a decorator for a story. I'm not sure that docs supports decorators, though.

csf question / support

All 6 comments

Yes, CSF default exports should be plain objects. You can see more discussion here: https://github.com/storybookjs/storybook/pull/10959#pullrequestreview-431120762

Thanks @shilman. Would you be able to suggest an alternative way of adding a component to every docs page? Ideally I'd like to add one component without altering the default page order. Is there such a thing as decorators for docspages?

Yes there's a wrapper for the docs page: https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/recipes.md#overwriting-docs-container

I would probably use story parameters:

export default { 
  title: 'blah',
  parameters: {
    componentStatus: 'deprecated'
  }
}
...

Then define a new DocBlock and add it globally in .storybook/main.js (untested):

import React, { FC, useContext } from 'react';
import { DocsContext } from '@storybook/addon-docs/blocks';

const StatusBadge: FC<{}> = () => {
  const { parameters: { componentStatus = 'unknown' } } = useContext(DocsContext);
  return <>{componentStatus}</>;
};

import { StatusBadge }  from '
export const  parameters = {
    docs: {
      page: () => (
        <>
          <StatusBadge />
          <DocBlocks.Title />
          <DocBlocks.Subtitle />
          <DocBlocks.Description />
          <DocBlocks.Primary />
          <DocBlocks.Props />
          <DocBlocks.Stories />
        </>
      ),
    },
  },
};

cc @domyen WDYT about building something like this in?

I support a feature that allows you to set componentStatus and get a badge. Most professional design systems have this pattern.

Loop me in :)

Thanks @shilman, the thought of default parameters didn't cross my mind but it makes total sense.

I instead found out that docs page decorators _do_ exist, and created one that parses a tag out of the component's JSDoc. e.g. @status stable


/**
 * Decorator to display a component's status in its docspage.
 */
export default addParameters({
  docs: {
    page: (props: DocsPageProps) => {
      const context = useContext(DocsContext);

      // Ideally parsing the status could be done when the babel loader
      // initally generates __docgenInfo
      let status = context.parameters.component.__docgenInfo.status as ComponentStatus;
      if (!context.parameters.component.__docgenInfo.status) {
        const description: string = context.parameters.component.__docgenInfo.description ?? "";
        const statusMatch = description.match(/@status\s+(.*)/);

        status = (statusMatch?.[1] ?? "") as ComponentStatus;
        context.parameters.component.__docgenInfo.description = description.replace(statusMatch?.[0]!, `\nComponent status: ${status}`)
        context.parameters.component.__docgenInfo.status = status;
      }

      return (
        <>
          <StatusBadge status={status} />
          <DocsPage {...props} />
        </>
      );
    },
  },
});

I'm sure it could be made more robust by customising the docgen step to parse the description further, but I'm happy something is working for now :)

@rikkit Fixed a typo in my code above default => const

Was this page helpful?
0 / 5 - 0 ratings

Related issues

enagy27 picture enagy27  路  69Comments

EdenTurgeman picture EdenTurgeman  路  81Comments

tycho01 picture tycho01  路  76Comments

ilyaulyanov picture ilyaulyanov  路  100Comments

dmmarmol picture dmmarmol  路  57Comments