storybookjs / storybook

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

addon-info doesn't show Proptypes and description #2708

Closed AhmadBehzadi closed 6 years ago

AhmadBehzadi commented 6 years ago

Addon-info doesn't show Proptypes and description

Hi, I have problem with addon-info when I use my component as an external package. There is no problem when I copy the component source into my storybook project, but when call it from node module the PropType and description does not show

bug

My Component source code :

import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import BreadcrumbStyled from './style/BreadcrumbStyled';
import OlStyled from './style/OlStyled';
import LiStyled from './style/LiStyled';

class Breadcrumb extends React.Component {
  render() {

    const {
      items, primary, secondary, info, success, danger, warning, rtl
    } = this.props;
    const elements = [];
    for (let i = 0; i < items.length - 1; i += 1) {
      elements.push(items[i]);
    }
    const lastElement = items[items.length - 1];
    const themeProps = {
      primary, secondary, info, success, danger, warning, rtl
    };
    return (
      <BreadcrumbStyled {...this.props}>
        <OlStyled {...themeProps}>
          {elements.map((item, index) => {
            return (
              <LiStyled
                key={index}
                {...themeProps}
              >
                <Link
                  to={item.path
                }>
                  {item.name}
                </Link>
              </LiStyled>
            );
          })}
          <LiStyled {...themeProps}>{lastElement.name}</LiStyled>
        </OlStyled>
      </BreadcrumbStyled>
    );
  }
}
Breadcrumb.propTypes = {
  /** array of objects */
  items: PropTypes.array.isRequired,
  /** rtl is true component show  in right side of the window, default is false (from left side). */
  rtl: PropTypes.bool,
  /** Boolean indicating whether the component renders with Theme.primary color */
  primary: PropTypes.bool,
  /** Boolean indicating whether the component renders with Theme.secondary color */
  secondary: PropTypes.bool,
  /** Boolean indicating whether the component renders with Theme.info color */
  info: PropTypes.bool,
  /** Boolean indicating whether the component renders with Theme.warning color  */
  warning: PropTypes.bool,
  /** Boolean indicating whether the component renders with Theme.danger color  */
  danger: PropTypes.bool,
  /** Boolean indicating whether the component renders with Theme.success color */
  success: PropTypes.bool,
  /** The inline-styles for the root element. */
  style: PropTypes.object,
  /** The className for the root element. */
  className: PropTypes.string,
  /** The color renders with Theme.foreColor . */
  foreColor: PropTypes.string
};
Breadcrumb.defaultProps = {
  rtl: false,
  primary: false,
  secondary: false,
  info: false,
  warning: false,
  danger: false,
  success: false,
  style: {},
  className: '',
  foreColor: ''
};
export default Breadcrumb;

I tried both compiling and installing from npm and linking to source code in github, there was no different.

jsanchez034 commented 6 years ago

Same thing is happening to me, looks like it's because in the PrettyPropType component... https://github.com/storybooks/storybook/blob/master/addons/info/src/components/types/PrettyPropType.js#L34 The propType prop can be an object or a string and that prop is always destructured above. I will make a PR to fix this.

jsanchez034 commented 6 years ago

Sorry actually the root cause of the issue looks to be related to.. https://github.com/storybooks/babel-plugin-react-docgen/issues/33

thupi commented 6 years ago

I had trouble with getting this to work while using Typescript :-) Even though it is fairly easy to use Typescript with storybook it seams to break addon-info's propTypes and description. However, defaultProps and property still works :-)

wellyal commented 6 years ago

Any updates on this. I'm also facing this problem. :(

jsanchez034 commented 6 years ago

In my case the issue is only for stateless functional components defined as a regular function declaration, example...

import React, { PropTypes } from 'react'

const Child = () => (
  <div>Sample</div>
)

function FuncDeclaration({ children }) {
  return (
    <div>
      {children}
      <Child />
    </div>
  )
}

FuncDeclaration.propTypes = {
  children: PropTypes.node
}

export default FuncDeclaration

The issue has to do with babel-plugin-react-docgen. I created a PR in the babel-plugin-react-docgen repo to address my issue in particular... https://github.com/storybooks/babel-plugin-react-docgen/pull/41/files

kgwebsites commented 6 years ago

The issue for me is the method of import. It seems that destructuring the import is causing propType and Description table headers to go missing.

This does not work: import {Input} from '../lib/elements/Forms';

This does: import Input from '../lib/elements/form/Input';

naomiHauret commented 6 years ago

I'm having this problem with components that use CSS-Modules :confused: (more specifically React CSS-Modules)

AhmadBehzadi commented 6 years ago

@kgwebsites That didn't solve my problem

denchen commented 6 years ago

Hi, I'm seeing a similar issue, and I've narrowed it down to a case where I have multiple exports

Given this:

import React from 'react';
import PropTypes from 'prop-types';

class Test extends React.PureComponent {
  render() {
    return <button>test</button>;
  }
}

Test.propTypes = {
  /** Some description here */
  randomProp: PropTypes.string
};

export default Test;

export const Test2 = () => <div>Hi</div>;

The description will not show up for randomProp. HOWEVER, if I comment out the last line, as such:

// export const Test2 = () => <div>Hi</div>;

Then the description for randomProp will show up. This is even if my Story does not import Test2 at all.

I don't know if there are other situations where descriptions won't show up, but the scenario I described above is definitely reproducible.

Incidentally, a use case for my code having a default export as well as other exports is to have a Redux-connected component as well as its unconnected component available for testing.

denchen commented 6 years ago

Another curious case.

Docgen works for this:

import React from 'react';
import PropTypes from 'prop-types';

const Test = () => <button>A button</button>;

Test.propTypes = {
  /** Another label */
  randomProp: PropTypes.string
};

export default Test;

But this doesn't:

import React from 'react';
import PropTypes from 'prop-types';

export const Test = () => <button>A button</button>;

Test.propTypes = {
  /** Another label */
  randomProp: PropTypes.string
};

Of course, in the storybook, I'm using import Test from 'Test'; in the first scenario and import { Test } from 'Test'; in the second.

Hypnosphi commented 6 years ago

I use my component as an external package

@AhmadBehzadi You probably need to remove this package from babel-loader exclude, so that docgen plugin gets applied to it. Alternatively, you can include babel-plugin-react-docgen in babel config for the component package

Hypnosphi commented 6 years ago

@denchen can you please open a separate issue? Looks like you are not importing your component as an external package, are you?

aaronincincy commented 6 years ago

I'm experiencing this issue as well. It looks in the prop table my propType is coming in as a string, but PrettyPropType is trying to destructure it into {name}.

denchen commented 6 years ago

@Hypnosphi I opened a new issue at #3059. My component is not an external package.

lordgiotto commented 6 years ago

I had a similar issue: propTypes and descriptions appear if I use a functional component, but doesn't work if I use a class component (only properties names are displayed).

Neglexis commented 6 years ago

I'm having the same issue.

This is my current setup that already fails.

Do not however that the defaultProps and isRequired are getting picked up, so no idea what I'm doing wrong.

Test Component

import React from 'react';
import PropTypes from 'prop-types';

const Test = ({ name }) => {
  return <div>Hello {name}</div>;
};

Test.propTypes = {
  /** This should be the description */
  name: PropTypes.string.isRequired,
};

Test.defaultProps = {
  name: 'mars',
};

export default Test;

Index.js

import React from 'react';
import { storiesOf } from '@storybook/react';
import Test from 'Framework/Test';
import '../assets/stylesheets/kayzrfont.css';
import { withInfo } from '@storybook/addon-info';

storiesOf('Test', module).add(
  'Default',
  withInfo(`
      description or documentation about my component, supports markdown

      ~~~js
      <Button>Click Here</Button>
      ~~~

    `)(() => <Test name="world" />),
);

Storybook output

image

If you need any more info @Hypnosphi, don't hesitate to ping me!

kgwebsites commented 6 years ago

Well if you put .isRequired on a PropType, you cannot declare a defaultProp

On Mar 12, 2018, at 8:26 AM, Jasper Dansercoer notifications@github.com wrote:

I'm having the same issue.

This is my current setup that already fails.

Do not however that the defaultProps and isRequired are getting picked up, so no idea what I'm doing wrong.

Test Component

import React from 'react'; import PropTypes from 'prop-types';

const Test = ({ name }) => { return

Hello {name}
; };

Test.propTypes = { /* This should be the description / name: PropTypes.string.isRequired, };

Test.defaultProps = { name: 'mars', };

export default Test; Index.js

import React from 'react'; import { storiesOf } from '@storybook/react'; import Test from 'Framework/Test'; import '../assets/stylesheets/kayzrfont.css'; import { withInfo } from '@storybook/addon-info';

storiesOf('Test', module).add( 'Default', withInfo(` description or documentation about my component, supports markdown

  ~~~js
  <Button>Click Here</Button>
  ~~~

`)(() => <Test name="world" />),

); Storybook output

If you need any more info @Hypnosphi, don't hesitate to ping me!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

Neglexis commented 6 years ago

@kgwebsites I know, but it was just to illustrate the point that those settings do work. If I only declare one of them, the propType and description column still won't work.

loadingwyn commented 6 years ago

I'm facing this problem too!

sauldeleon commented 6 years ago

I think I am facing the same issue. I am not using defaultProps in my components, and some of set correctly the propTable in the info section, but with others I cant see the description neither the proptype :/

Trying to find some behaviour..

thanks!

EDIT: ok, found something strange.

As I am using redux, some of my components have

export ComponenName
export default connect(null, null)(ComponentName)

When this happens, the proptypes arent show correctly. If I delete the default export, it will work.

danielduan commented 6 years ago

We do not support multiple exports within the same file with Info documentation at the moment. It's a limitation with our babel plugin and the way react-docgen works.

https://github.com/storybooks/babel-plugin-react-docgen/pull/46

sauldeleon commented 6 years ago

Thanks for your response. What i am doing now is generating the .__docgen manually.

I remove the default export, add a console.log(JSON.Stringify(myComponent.docgeninfo)) in the storybook and then copy this log, and add It again as myComponent.docgeninfo = JSON.parse(logText).

Then I add again the default export to the original component file.

It's a bit weird but it works :)

Edit: Just to clarify for future readers. Here is my workarround:

  1. On the desired component, remove the default export
  2. Write something like this in your storybook
stories.add(
  'default',
  withInfo({
    text: `
  <div>
    <h2>My component description with nice html</h2>
    <p>
      A description
    </p>
  </div>
  `,
  })(() => {
    console.log(JSON.stringify(MyComponent.__docgenInfo))
    return <MyComponent/>
  })
)
  1. Copy the console output. For comodity, I use to go to a website like this, I paste the output, process it, and copy the parsed result.
  2. Back to the code, in the original component's code, I add again the export default line we deleted in point 1
  3. Back to the Storybook's code, it ends like this:
stories.add(
  'default',
  withInfo({
    text: `
  <div>
    <h2>My component description with nice html</h2>
    <p>
      A description
    </p>
  </div>
  `,
  })(() => {
     if (!MyComponent.__docgenInfo) {
      MyComponent.__docgenInfo = THE_JSON_PARSED_OUTPUT
    }
    return <MyComponent/>
  })
)

Obviously this is not automated, but for that of us that have tests and also redux connected components, it's a way of having the proptypes in the storybook. The if clause remains because maybe in the future the babel-plugin-react-docgen could export proptypes from Components with 2 exports.

If you change the component, you will have to modify the storybook also at the same time. But it will be a minor change.

Hope this could help somebody.

Thanks again

stale[bot] commented 6 years ago

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

Neglexis commented 6 years ago

No, this is everything but stale. The real issue wasn't answered in @danielduan's reply.

Hypnosphi commented 6 years ago

Please try it with 3.4.1 AFAIK there were some huge fixes in that field

Neglexis commented 6 years ago

Still nothing.

Still fails with this component.

import React from 'react';
import PropTypes from 'prop-types';

const Test = ({ name }) => {
  return <div>Hello {name}</div>;
};

Test.propTypes = {
  /** This should be the description */
  name: PropTypes.string.isRequired,
};

Test.defaultProps = {
  name: 'mars',
};

export default Test;

image

EDIT: Again, the combination of defaultProps and isRequired is purely to demonstrate that they both work. Removing one of those, still doesn't make the description or propType show up.

velidan commented 6 years ago

I also have the same issue. So the problem is actual.

cprakti commented 6 years ago

☝️ also have this issue

danielduan commented 6 years ago

Can you guys create new issues in the babel plugin repo with some examples of components that don't get detected? https://github.com/storybooks/babel-plugin-react-docgen

There are many different variants as to how people compose and export stateless components, and it's hard to track every single case. This is compounded by the fact that Facebook's react-docgen uses a different AST walker than Babel so we can't directly use their walkers.

As a bonus, I would love PRs to that repository to fix individual issues. It's pretty time consuming to test each individual case and I'm always looking for more help. Thanks!

As a temporary workaround, you can define your own info by using Component.__docgenInfo = {}. Take a look at the format react-docgen generates.

mcissel commented 6 years ago

I fixed this by 'extending' the PropTable component from storybook/addon-info.

import React from 'react';
import PropTable from '@storybook/addon-info/dist/components/PropTable';

const PropTypesTable = ({ propDefinitions, ...props }) => {
  propDefinitions.forEach((def) => {
    if (typeof def.propType === 'string') {
      def.propType = { name: def.propType };
    }
  });

  return <PropTable propDefinitions={propDefinitions} {...props} />;
};

export default PropTypesTable;

Then override the TableComponent in storybook/config.js

import { setDefaults } from '@storybook/addon-info';

setDefaults({
  TableComponent: PropTypesTable, // Override the component used to render the props table
});
icynoangel commented 6 years ago

@mcissel Nice! That works, thank you!

niecnasowa commented 6 years ago

It started working properly for me when I installed:

ewagulik commented 6 years ago

Niecnasowa, worked for me, thanks :)

rafal-r commented 6 years ago

Maybe when your package resides in node_modules or outside project directory then weback treats those files on import as production builds which results in omitting comments and propTypes ?

My solution was to make symlink to my module (which was outside project directory) and then import components from that directory. This approach requires disabling symlinks in webpack resolver config: const path = require("path"); module.exports = (storybookBaseConfig, configType, defaultConfig) => { defaultConfig.resolve.symlinks = false; return defaultConfig; };

With this configuration my module written in ES6 syntax is fully parseable by webpack, propTypes and descriptions are visible in storybook and app is reloaded on every file change in module directory.

JuhQ commented 6 years ago

It seems I as well have this issue. I have it with pure components and higher order functions. Hopefully some resolution will be found in future versions of the addon.

Edit: it appears that my problem is caused by the fact that my component is not directly returning jsx but instead returning a function call which then returns the jsx.

So this doesn't work (not tested pseudo component)

function myComponent({firstProp, secondProp}) {
  return functionCall(firstProp, secondProp);
};

function functionCall(firstProp, secondProp) {
  return <div>{firstProp} {secondProp}</div>;
}

But this works

function myComponent({firstProp, secondProp}) {
  return <div>{firstProp} {secondProp}</div>
};

In many cases the first one is the desired use case and in any case storybook should not define how code is structured in a project.

danielduan commented 6 years ago

op's example is added to https://github.com/storybooks/babel-plugin-react-docgen/pull/54 and fixed

mikelax commented 6 years ago

@danielduan I see this issue is closed, has it been released in version 3.4.10? I am still seeing the problem in both 3.4.8 and 3.4.10. I have not yet tried any of the 4.x betas.

Jony-Y commented 6 years ago

@niecnasowa your solution worked for me.

davidcalhoun commented 5 years ago

Still had this issue even with the latest versions (see list below). @niecnasowa's solution worked for me - I simply added babel-plugin-react-docgen (add to .babelrc "plugins": ["react-docgen"]) without having to add react-docgen

Package Version info:

    "@storybook/addon-actions": "^4.1.2",
    "@storybook/addon-centered": "^4.1.2",
    "@storybook/addon-info": "^4.1.2",
    "@storybook/addon-knobs": "^4.1.2",
    "@storybook/addon-links": "^4.1.2",
    "@storybook/addon-options": "^4.1.2",
    "@storybook/addons": "^4.1.2",
    "@storybook/cli": "^4.1.2",
    "@storybook/react": "^4.1.2",

Code example that was failing:

export default class SomeComponent extends PureComponent {
    static propTypes = {
        /** Test comment. */
        foo: PropTypes.string
    };
}
tomasskopal commented 5 years ago

@niecnasowa Worked for me. 👍 Note that if you have more plugins in .babelrc the "react-docgen" must be first.

artyomtrityak commented 5 years ago

It still does not work for me with docgen

cassiewang01 commented 5 years ago

It still does not work for me with docgen @niecnasowa

Lory1990 commented 5 years ago

This does not work for me @niecnasowa

{
  "presets": [
    "@babel/preset-react",
    ["@babel/preset-env", { "useBuiltIns": "entry", "corejs": 3 }]
  ],
  "plugins": [
      "react-docgen",
      "@babel/plugin-proposal-class-properties",
     "babel-plugin-module-resolver"
  ]
}
khainguyenvan commented 4 years ago

In my case, I currently added the storybook to main project. My solution is extends a custom webpack config file inside storybook folder and replace jsx rules with this code and its work.

const path = require('path');
const webpack = require('webpack');
const custom = require('../webpack.config.js');

module.exports = ({ config, mode }) => {
    const rules = custom.module.rules.filter(rule => rule.loader === 'babel-loader')
    return {
        ...config,
        module: {
            ...config.module,
            rules: [
                ...rules ,
                {
                    test: /\.jsx?$/,
                    loader: 'babel-loader',
                    exclude: /node_modules/,
                    query: {
                        cacheDirectory: true,
                        presets: ['@babel/preset-react'],
                        plugins: ["babel-plugin-react-docgen"]
                    },
                },
            ]
        },
    };
};
shilman commented 4 years ago

Addon-info is being superceded by addon-docs, which fixes a bunch of bugs and is easier to maintain. Please give it a try! https://medium.com/storybookjs/storybook-docspage-e185bc3622bf

jeroenreijs commented 4 years ago

We had this problem as well on components that had JSS. Because of the JSS wrapper, the columns for propType and description were empty. By exporting the 'raw' component and use that in the story, the problem was solved.

shilman commented 4 years ago

Addon-info is being superceded by addon-docs, which fixes a bunch of bugs and is easier to maintain. Please give it a try! https://medium.com/storybookjs/storybook-docspage-e185bc3622bf