storybookjs / storybook

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

Addon-Docs: Props table won't be generated for TypeScript types if forwardRef used with the default export #9511

Closed SirMoustache closed 4 years ago

SirMoustache commented 4 years ago

Describe the bug If forwardRef used without named export and with only default one, then no props table will be generated.

For example, this:

Button.tsx

import React, { forwardRef } from 'react';

interface ButtonProps {
  /**
   * Sets the button size.
   */
  variant?: 'small' | 'large';
  /**
   * Disables the button.
   */
  disabled?: boolean;
}

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ disabled = false, variant = 'small', children }, ref) => (
    <button disabled={disabled} ref={ref}>
      {children} {variant}
    </button>
  ),
);

export default Button;

Button.stories.tsx

import React, { FC } from 'react';
import Button from './Button';

export default { title: 'Button', component: Button };

export const Default: FC = () => <Button>I'm a button!</Button>;

Will lead to this result:

image

But if I add export, a props table will be generated. No need to change import from default to named, jus add export.

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ disabled = false, variant = 'small', children }, ref) => (
    <button disabled={disabled} ref={ref}>
      {children} {variant}
    </button>
  ),
);

image

Expected behavior A props table should be generated

Packages

{
    "@storybook/addon-actions": "^5.3.5",
    "@storybook/addon-docs": "^5.3.5",
    "@storybook/addon-links": "^5.3.5",
    "@storybook/addons": "^5.3.5",
    "@storybook/preset-create-react-app": "^1.5.1",
    "@storybook/preset-typescript": "^1.2.0",
    "@storybook/react": "^5.3.5"
}

main.js

module.exports = {
  stories: ['../src/**/*.stories.(tsx|mdx)'],
  addons: [
    {
      name: '@storybook/preset-create-react-app',
      options: {
        tsDocgenLoaderOptions: {},
      },
    },
    {
      name: '@storybook/addon-docs',
      options: {
        configureJSX: true,
      },
    },
  ],
};
Zunaib commented 4 years ago

Hi, You are missing the export keyword before the Component's name. You have to export const {Component} too, as react-docgen-typescript-loader or react-docgen-loader has not added support for forwardRef's yet.

import React, { forwardRef } from 'react';

interface ButtonProps {
  /**
   * Sets the button size.
   */
  variant?: 'small' | 'large';
  /**
   * Disables the button.
   */
  disabled?: boolean;
}

//Added export here
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ disabled = false, variant = 'small', children }, ref) => (
    <button disabled={disabled} ref={ref}>
      {children} {variant}
    </button>
  ),
);

export default Button;
MateusAndrade commented 4 years ago

Thanks @Zunaib , applying the docs-gen at webpack fixed that.

For anybody having this problem, you just need to add a docs gen for TS after you TS loader. Like that:

      {
        test: /\.tsx?$/,
        include: path.resolve(__dirname, '../src'),
        loader: 'awesome-typescript-loader', //any loader you want
      },
      {
        test: /\.tsx?$/,
        include: path.resolve(__dirname, '../src'),
        loader: 'react-docgen-typescript-loader',
      },

Then it worked like a charm! 💃

Screen Shot 2020-01-19 at 12 49 39

Edit: If someone wants a Live Example, please refer to: https://github.com/viniarruda/react-ecommerce/tree/master/packages/design-system

Zunaib commented 4 years ago

Happy To Help

stale[bot] commented 4 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!

shilman commented 4 years ago

Repro: https://github.com/storybookjs/storybook/commit/1032b774211f15e67c3f9a01b28633a44a83a2d3

taze-fullstack commented 4 years ago

@shilman chiming in on this with the CRA + TS template (not able to have a customized webpack to apply @MateusAndrade's solution :<)

Also not sure if this is actually a separate issue but I feel like this is very closely related to this one so I'll just comment here.

Without forwardRef:

image

export const Hello = ({title, children}: HelloProps) => {
  return <div className="hello">Hello Component {title} {children}</div>;
};

With forwardRef:

image

export const Hello = forwardRef<HTMLDivElement, HelloProps>(
  ({title, children}, ref) => {
    return (
      <div className="hello" ref={ref}>
        Hello Component {title} {children}
      </div>
    );
  }
);

Update: figured it out 😅 add :Interface to the destruction {x,y} as in: image

export const Hello = forwardRef<HTMLDivElement, HelloProps>(
  ({title, children}: HelloProps, ref) => {
    return (
      <div className="hello" ref={ref}>
        Hello Component {title} {children}
      </div>
    );
  }
);
thany commented 4 years ago

If I add an export, I get only two props in the props table: ref and key. Both are not useful to display there. I actually have a whole interface defined that is still completely ignored.

export const FormInputCheckbox = React.forwardRef<Ref, React.PropsWithChildren<IFormInputCheckRadio>>

At least the error is gone, but we're not there yet.

stale[bot] commented 4 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!

stale[bot] commented 4 years ago

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

shilman commented 4 years ago

FYI the recommended 6.0 setup (should work in 5.3 too) is ready, and is the default in the most recent versions of both @storybook/preset-typescript and @storybook/preset-create-react-app:

https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#react-prop-tables-with-typescript

I am commenting on all props-related issues to request that you upgrade so we can get this right before release. Thanks!

Also, Args-based props tables are available in 6.0 alpha:

https://gist.github.com/shilman/69c1dd41a466bae137cc88bd2c6ef487

thany commented 4 years ago

Sooo, what should I actually do, to try out the new setup? Currently our SB instance still uses RDTL and removing it from the config.module.rules simply breaks all props tables.

I'm on SB 5.3.18.

/edit And if I replace it with BPRD, running storybook floods my terminal with a whole bunch of these errors:

ERROR in ./src/components/inputs/controls/FormInputTextarea.tsx
Module build failed (from ./node_modules/ts-loader/index.js):
TypeError: s.codePointAt is not a function

/edit2 Tried two more things. I added the @storybook/preset-typescript to addons in main.js. I saw other presets in that migration doc so I assume this might be correct. I don't know where else to put it, because that too, is not specified. Then, I removed RDTL again from the typescript rule in the same config file. The result: every error in the world is flooded into my terminal! It looks like all variables andsuch are now considered to be of type any, which is not true, and these errors obviously don't appear in my editor, and also not before making the above change.

Then I tried removing the whole typescript rule from main.js, in the assumption that the presets adds one for me. I was gravely mistaken again, as now I get two sets of errors. First, a whole lot of these:

ERROR in C:/workspaces/enexis-ui/node_modules/@types/react-native/globals.d.ts(40,15):
TS2300: Duplicate identifier 'FormData'.

And more, all on .d.ts files, none of them on any code we've typed. And then a bunch of syntax errors on some of the typescript files. It seems like it doesn't know how to deal with object?.value and something = that ?? those. But my babel config is set up to handle that perfectly fine. Again, these errors did not appears before making the above change, and yes, the code in question worked absolutely fine. So it's the presets messing things up for me.

So what do I do?

shilman commented 4 years ago

@thany do you have a repro I can look at?

thany commented 4 years ago

@shilman, I'm sorry, but our project is private. I don't have another project that reproduces the problem. I think mosty I just need to know how I'm supposed to implement BPRD, because the migration guide doesn't speak of it. It just says "use BPRD", basically, but how?

gius commented 3 years ago

I had the same problem with the 6.1.20 version. It turned out you should not use React.xxx reference and directly use the decomposed member xxx:

// does not work
export const Button: React.FunctionComponent<ButtonProps> = props => {...}

// works
export const Button: FunctionComponent<ButtonProps> = props => {...}

// does not work
export const Button3 = React.forwardRef<RawButton, ButtonProps>((props, ref) => {...}

// works
export const Button = forwardRef<RawButton, ButtonProps>((props, ref) => {...}

Moreover, just changing the type while Storybook is running in watch mode does not work. You need to restart the storybook (or force a full recompilation of the file).

GreLI commented 2 years ago

If someone got here in search of why props aren't generated as it did for me, try instead of writing

export default forwardRef(Component)

Do it this way:

const Component = forwardRef((props: Props, ref: Ref<HTMLWhateverElement>) => /* implementation */)

export default Component
iwan-uschka commented 6 months ago

https://github.com/storybookjs/storybook/issues/18136 is somehow related and might help someone.