storybook: [Bug]: V7, controls description not showing, TypeScript, Vite, react-docgen-typescript

Describe the bug

Every component in Storybook has TypeScript types where every prop has JSDoc comment. image

Previously in Storybook 6 everything worked correctly. All controls had description and default values shown and they were populated correctly via react-docgen-typescript. But after migrating to Storybook 7 and going “full Vite” controls are not showing any description. image

I also tried Webpack5 with Storybook 7 and seems that description did show up:

image

I’ve also switched between react-docgen and react-docgen-typescript and react-docgen did work better, but still some comments/description were missing.

To Reproduce

The project is private, don’t have rights to share it.

System

System:
    OS: macOS 13.2.1
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
  Binaries:
    Node: 16.19.1 - ~/.nvm/versions/node/v16.19.1/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v16.19.1/bin/yarn
    npm: 8.19.3 - ~/.nvm/versions/node/v16.19.1/bin/npm
  Browsers:
    Chrome: 114.0.5735.133
    Safari: 16.3
  npmPackages:
    @storybook/addon-a11y: ^7.1.0-alpha.32 => 7.1.0-alpha.33 
    @storybook/addon-actions: ^7.1.0-alpha.32 => 7.1.0-alpha.33 
    @storybook/addon-docs: ^7.1.0-alpha.32 => 7.1.0-alpha.33 
    @storybook/addon-essentials: ^7.1.0-alpha.32 => 7.1.0-alpha.33 
    @storybook/addon-links: ^7.1.0-alpha.32 => 7.1.0-alpha.33 
    @storybook/blocks: ^7.1.0-alpha.32 => 7.1.0-alpha.33 
    @storybook/client-api: ^7.1.0-alpha.32 => 7.1.0-alpha.33 
    @storybook/react: ^7.1.0-alpha.32 => 7.1.0-alpha.33 
    @storybook/react-vite: ^7.1.0-alpha.32 => 7.1.0-alpha.33

Additional context

I have a monorepo setup and Storybook collects all the story files correctly. These are the settings:

// apps/workshop/.storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';
import remarkGfm from 'remark-gfm';

const conf: StorybookConfig = {
  stories: [
    '../src/*.mdx',
    '../src/hooks/*.mdx',
    '../../../packages/**/*.stories.@(js|jsx|ts|tsx|mdx)'
  ],
  staticDirs: ['./static'],
  framework: {
    name: '@storybook/react-vite',
    options: {}
  },
  addons: [
    {
      name: '@storybook/addon-essentials',
      options: {
        docs: true,
        actions: true,
        backgrounds: true,
        controls: true
      }
    },
    {
      name: '@storybook/addon-docs',
      options: {
        transcludeMarkdown: true,
        mdxPluginOptions: {
          mdxCompileOptions: {
            // Styled tables, etc.
            remarkPlugins: [remarkGfm]
          }
        }
      }
    },
    '@storybook/addon-a11y',
    'storybook-addon-designs'
  ],
  typescript: {
    check: true,
    // react-docgen-typescript not working atm
    reactDocgen: 'react-docgen-typescript',
    reactDocgenTypescriptOptions: {
      shouldExtractLiteralValuesFromEnum: true,
      shouldRemoveUndefinedFromOptional: true,
      propFilter: (prop) =>
        prop.parent ? !/node_modules/.test(prop.parent.fileName) : true
    }
  },
  docs: {
    autodocs: true
  }
};

export default conf;
// apps/workshop/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), tsconfigPaths()],
  resolve: {
    alias: [
      {
        // https://github.com/vitejs/vite/issues/5764
        // this is required for the SCSS modules
        find: /^~(.*)$/,
        replacement: '$1'
      }
    ]
  }
});

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 5
  • Comments: 15 (2 by maintainers)

Most upvoted comments

This same happened in my monorepo. It also happened in clean React + Vite + Storybook setup project. If you have following in your Storybook config then properties are not getting picked up. Using displayName or forwardRef made no difference.

  typescript: {
    reactDocgen: 'react-docgen-typescript',
    reactDocgenTypescriptOptions: {
      compilerOptions: {
        allowSyntheticDefaultImports: false,
        esModuleInterop: false,
      },
    },
  },

Also if compilerOptions is just empty {} then it also doesn’t work. Removing compilerOptions field fixes everything. This issue was not present in Storybook 6. This is problematic because react-docgen-typescript specifically recommends to set those options for better speed but currently that completely breaks everything.

I had the same issue, where the argsTypes are not properly inferred. I have storybook running in a independent package inside a mono-repository. So the solution has been adding the following to my main.ts configuration.

{
  // Other configuration...
  typescript: {
    reactDocgen: "react-docgen-typescript",
    reactDocgenTypescriptOptions: {
      include: ["../../path/to/your/lib/**/**.tsx"], // <- This is the important line. 
    }
  }
}

@Yangseungchan, not resolved yet. I get some better result with react-docgen instead of a react-docgen-typescript, but it still missing out a lot of comments and some types can’t be resolve at all. I found another thread that has similar discussion, but looks like this is a common issue with the monorepos at the moment.

In the latest version ^8.0.8, the forwardRef situation still exists, and it cannot automatically create automatic documents for me.

I use storybook in Nextjs. image

This is my component: image

@Kureyko hey I just had a similar issue and somehow managed to find a workaround.

Do any of your components (ImageBanner, ImageBanner.Content, or ImageBanner.Image) happen to assign its displayName after their declarations? For example, like ImageBanner.displayName = 'ImageBanner'. In my case I was using React.forwardRef and I was passing the component to be wrapped as an anonymous function, which triggered react/display-name eslint rule, so I just slapped MyComponent.displayName = 'MyComponent' after the declaration to suppress the red squiggly lines… which was, to my surprise, interfering with the docgen process (I use react-docgen-typescript). So after commenting out MyComponent.displayName = 'MyComponent', the docgen worked as expected again.

Here are some reproducible code snippets:

// src/stories/MyComponent.tsx
import React from "react";

type Props = {
  /**
   * This is Prop1
   */
  prop1?: string;
  /**
   * This is Prop2
   */
  prop2?: boolean;
};

export default function MyComponent(props: Props) {
  return (
    <div>
      <SubComponent1 />
      <SubComponent2 />
    </div>
  );
}

const SubComponent1 = React.forwardRef(() => {
  return <div>sub1</div>;
});

// Toggle on/off the following line and re-start your storybook dev-server to see the effect.
// SubComponent1.displayName = "SubComponent1";

const SubComponent2 = React.forwardRef(() => {
  return <div>sub2</div>;
});

// Toggle on/off the following line and re-start your storybook dev-server to see the effect.
// SubComponent2.displayName = "SubComponent2";

// src/stories/MyComponent.stories.ts
import type { Meta, StoryObj } from "@storybook/react";
import MyComponent from "./MyComponent";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta = {
  component: MyComponent,
  tags: ["autodocs"],
} satisfies Meta<typeof MyComponent>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Basic: Story = {
  args: {
    prop1: "1",
    prop2: true,
  },
};

For core maintainers

@shilman Hey guys, this issue seems to be caused by generateDocgenCodeBlock() in vite-plugin-react-docgen-typescript. Specifically, it seems like when displayName prop of a React component is assigned manually (like in the example above), generateDocgenCodeBlock() produces code that attaches __docgenInfo to the wrong component (e.g., produces SubComponent1.__docgenInfo = {...} instead of MyComponent.__docgenInfo = {...}).

I’ll share the generated code below:

No manual displayName assignment (see the try block at the end)

'import { jsxDEV } from "react/jsx-dev-runtime";
import RefreshRuntime from "/@react-refresh";
const inWebWorker = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope;
let prevRefreshReg;
let prevRefreshSig;
if (import.meta.hot && !inWebWorker) {
  if (!window.__vite_plugin_react_preamble_installed__) {
    throw new Error("@vitejs/plugin-react can't detect preamble. Something is wrong. See https://github.com/vitejs/vite-plugin-react/pull/11#discussion_r430879201");
  }
  prevRefreshReg = window.$RefreshReg$;
  prevRefreshSig = window.$RefreshSig$;
  window.$RefreshReg$ = (type, id) => {
    RefreshRuntime.register(type, "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx " + id);
  };
  window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
}
import React from "react";
export default function MyComponent(props) {
  return /* @__PURE__ */ jsxDEV("div", { children: [
    /* @__PURE__ */ jsxDEV(SubComponent1, {}, void 0, false, {
      fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
      lineNumber: 15,
      columnNumber: 7
    }, this),
    /* @__PURE__ */ jsxDEV(SubComponent2, {}, void 0, false, {
      fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
      lineNumber: 16,
      columnNumber: 7
    }, this)
  ] }, void 0, true, {
    fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
    lineNumber: 14,
    columnNumber: 10
  }, this);
}
_c = MyComponent;
const SubComponent1 = React.forwardRef(_c2 = () => {
  return /* @__PURE__ */ jsxDEV("div", { children: "sub1" }, void 0, false, {
    fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
    lineNumber: 21,
    columnNumber: 10
  }, this);
});
_c3 = SubComponent1;
const SubComponent2 = React.forwardRef(_c4 = () => {
  return /* @__PURE__ */ jsxDEV("div", { children: "sub2" }, void 0, false, {
    fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
    lineNumber: 28,
    columnNumber: 10
  }, this);
});
_c5 = SubComponent2;
var _c, _c2, _c3, _c4, _c5;
$RefreshReg$(_c, "MyComponent");
$RefreshReg$(_c2, "SubComponent1$React.forwardRef");
$RefreshReg$(_c3, "SubComponent1");
$RefreshReg$(_c4, "SubComponent2$React.forwardRef");
$RefreshReg$(_c5, "SubComponent2");
if (import.meta.hot && !inWebWorker) {
  window.$RefreshReg$ = prevRefreshReg;
  window.$RefreshSig$ = prevRefreshSig;
  RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
    RefreshRuntime.registerExportsForReactRefresh("/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx", currentExports);
    import.meta.hot.accept((nextExports) => {
      if (!nextExports)
        return;
      const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports);
      if (invalidateMessage)
        import.meta.hot.invalidate(invalidateMessage);
    });
  });
}
try {
    // @ts-ignore
    MyComponent.displayName = "MyComponent";
    // @ts-ignore
    MyComponent.__docgenInfo = { "description": "", "displayName": "MyComponent", "props": { "prop1": { "defaultValue": null, "description": "This is Prop1", "name": "prop1", "required": false, "type": { "name": "string" } }, "prop2": { "defaultValue": null, "description": "This is Prop2", "name": "prop2", "required": false, "type": { "name": "boolean" } } } };
}
catch (__react_docgen_typescript_loader_error) { }'

Manual displayName assignment (see the try block at the end)

'import { jsxDEV } from "react/jsx-dev-runtime";
import RefreshRuntime from "/@react-refresh";
const inWebWorker = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope;
let prevRefreshReg;
let prevRefreshSig;
if (import.meta.hot && !inWebWorker) {
  if (!window.__vite_plugin_react_preamble_installed__) {
    throw new Error("@vitejs/plugin-react can't detect preamble. Something is wrong. See https://github.com/vitejs/vite-plugin-react/pull/11#discussion_r430879201");
  }
  prevRefreshReg = window.$RefreshReg$;
  prevRefreshSig = window.$RefreshSig$;
  window.$RefreshReg$ = (type, id) => {
    RefreshRuntime.register(type, "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx " + id);
  };
  window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;
}
import React from "react";
export default function MyComponent(props) {
  return /* @__PURE__ */ jsxDEV("div", { children: [
    /* @__PURE__ */ jsxDEV(SubComponent1, {}, void 0, false, {
      fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
      lineNumber: 15,
      columnNumber: 7
    }, this),
    /* @__PURE__ */ jsxDEV(SubComponent2, {}, void 0, false, {
      fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
      lineNumber: 16,
      columnNumber: 7
    }, this)
  ] }, void 0, true, {
    fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
    lineNumber: 14,
    columnNumber: 10
  }, this);
}
_c = MyComponent;
const SubComponent1 = React.forwardRef(_c2 = () => {
  return /* @__PURE__ */ jsxDEV("div", { children: "sub1" }, void 0, false, {
    fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
    lineNumber: 21,
    columnNumber: 10
  }, this);
});
_c3 = SubComponent1;
SubComponent1.displayName = "SubComponent1";
const SubComponent2 = React.forwardRef(_c4 = () => {
  return /* @__PURE__ */ jsxDEV("div", { children: "sub2" }, void 0, false, {
    fileName: "/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx",
    lineNumber: 28,
    columnNumber: 10
  }, this);
});
_c5 = SubComponent2;
SubComponent2.displayName = "SubComponent2";
var _c, _c2, _c3, _c4, _c5;
$RefreshReg$(_c, "MyComponent");
$RefreshReg$(_c2, "SubComponent1$React.forwardRef");
$RefreshReg$(_c3, "SubComponent1");
$RefreshReg$(_c4, "SubComponent2$React.forwardRef");
$RefreshReg$(_c5, "SubComponent2");
if (import.meta.hot && !inWebWorker) {
  window.$RefreshReg$ = prevRefreshReg;
  window.$RefreshSig$ = prevRefreshSig;
  RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => {
    RefreshRuntime.registerExportsForReactRefresh("/Users/sook/Desktop/storybook-1/sandbox/react-vite-default-ts/src/stories/MyComponent.tsx", currentExports);
    import.meta.hot.accept((nextExports) => {
      if (!nextExports)
        return;
      const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports);
      if (invalidateMessage)
        import.meta.hot.invalidate(invalidateMessage);
    });
  });
}
try {
    // @ts-ignore
    SubComponent1.displayName = "SubComponent1";
    // @ts-ignore
    SubComponent1.__docgenInfo = { "description": "", "displayName": "SubComponent1", "props": { "prop1": { "defaultValue": null, "description": "This is Prop1", "name": "prop1", "required": false, "type": { "name": "string" } }, "prop2": { "defaultValue": null, "description": "This is Prop2", "name": "prop2", "required": false, "type": { "name": "boolean" } } } };
}
catch (__react_docgen_typescript_loader_error) { }'

@shilman However, even in version 8.0 react-docgen, we are having a lot of difficulty migrating using react-docgen due to the following issue. This issue is a fatal drawback for developers who customize UI libraries or frequently use forwardRef. Still, I can feel that performance has become faster after migrating to react-docgen, so I hope that this issue is resolved as soon as possible.

@siosio34 We have improved react-docgen and are recommending that as the default in 8.0. As for react-docgen-typescript the “help wanted” label means that we’d be happy to accept fixes from the community from this issue but have not prioritized it for the core team.

Using NX monorepo, React lib with Storybook v7.5.3 and Vite to build it. This worked for me:

const config: StorybookConfig = {
    ...
    typescript: {
        check: false,
        reactDocgen: 'react-docgen-typescript',
        reactDocgenTypescriptOptions: {
            shouldExtractLiteralValuesFromEnum: true,
            propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
        },
    },
    ...
};

in main.ts file and within each component I got rid of Component.displayName = 'Component' line and exposed both named and default export of Component from the same file.

Same issue here, don’t have any of the forwardRef stuff above. Description / controls not auto populated from my comments in the component.