storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
84.68k stars 9.32k forks source link

addon-docs: Description from docgen doesn't work when using React.memo #18136

Open siminino opened 2 years ago

siminino commented 2 years ago

Describe the bug I am trying show the description from a memoized component in docs page. Running react-docgen cli is possible to confirm that description it's being exported as expected, but it is not displayed at docs page.

To Reproduce Add description to any component and export it memoized. Ex:

/**
 * This is a Button Wrapper
 */
const Button = props => <button {...props} />

export default React.memo(Button)

System System: OS: macOS 12.3.1 CPU: (8) arm64 Apple M1 Pro Binaries: Node: 16.13.1 - ~/.nvm/versions/node/v16.13.1/bin/node Yarn: 1.22.18 - ~/.nvm/versions/node/v16.13.1/bin/yarn npm: 6.14.6 - ~/projects/sandbox/node_modules/.bin/npm Browsers: Chrome: 101.0.4951.54 Safari: 15.4 npmPackages: @storybook/addon-essentials: ^6.4.22 => 6.4.22 @storybook/react: ^6.4.22 => 6.4.22 @storybook/storybook-deployer: ^2.8.10 => 2.8.10

Cause of the issue The props are extracted properly by a conditional that checks if the component is memoized in docs/extractProps. In case, it uses component.type instead. The same conditional was not implemented for description. Description Block calls extractComponentDescription that always use the component to obtain the description.

Workaround Set Component.type on CSF export at story file. Ex:

export default {
  title: 'Button',
  component: Button.type,
}
a690700752 commented 2 years ago

+1, same issue

abder commented 2 years ago
export default {
  title: 'Button',
  component: Button.type,
}

How we can use this with Meta in MDX, it doesn't work for me

iwan-uschka commented 1 year ago

Component.type doesn't work for me unfortunately using

"@storybook/addon-actions": "^7.0.6",
"@storybook/addon-docs": "^7.0.6",
"@storybook/addon-essentials": "^7.0.6",
"@storybook/addon-links": "^7.0.6",
"@storybook/addon-mdx-gfm": "^7.0.6",
"@storybook/react": "^7.0.6",
"@storybook/react-webpack5": "^7.0.6",
"storybook": "^7.0.6",
toakleaf commented 1 year ago

Seeing some issue in 7.0.7. Also, when using React.memo the default values for props that normally show up in controls, as well as docstrings in the props that also show up in controls block are missing.

shilman commented 1 year ago

Can you try out the following canary in your project and let me know whether it resolves any of your issues (or generates new ones)? We're looking to switch the default docgen from react-docgen-typescript to react-docgen, which is much faster and may also fix some long-standing bugs. Many thanks!

Note that the change is currently only for Vite projects. Instructions in the "how to test" section: 👉 https://github.com/storybookjs/storybook/pull/23825

iwan-uschka commented 10 months ago

Just created a vite (5.0.8) project and added storybook (7.6.8) to it (including addon-docs). Unfortunately the issue still persists.

iwan-uschka commented 7 months ago

Same when using React.forwardRef. I was able to get the props table like described in https://github.com/storybookjs/storybook/issues/9511#issuecomment-953244988 but not the description.

iwan-uschka commented 7 months ago

I was able to get the props table and the description rendered in storybook for components wrapped with memo() or forwardRef() by exporting a helper from the component file containing the types and docs like

export interface ComponentProps {
  // ...
}

/**
 * Documentation goes here.
 */
export const __StorybookHelper_Component: typeof Component = () => null;
// 👆 this is new

const Component = (props: ComponentProps) => {
  // ...
}

export default memo(Component);// the same for forwardRef(Component)

and using both exports in the story like

import Component, {
  __StorybookHelper_Component,
} from '@repo/ui/path/to/Component';

import type { ComponentProps } from '@repo/ui/path/to/Component';

export default {
  component: __StorybookHelper_Component,
} as Meta;

const Template: StoryFn<ComponentProps> = componentProps => (
  <Component {...componentProps} />
);

export const Default: StoryFn<ComponentProps> = Template.bind({});

I consider this a workaround because of this additional export.