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

Created on 17 Jan 2020  路  13Comments  路  Source: storybookjs/storybook

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,
      },
    },
  ],
};

docs props inactive question / support

Most helpful comment

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;

All 13 comments

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;

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

Happy To Help

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 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>
    );
  }
);

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.

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!

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!

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

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?

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

@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?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

purplecones picture purplecones  路  3Comments

miljan-aleksic picture miljan-aleksic  路  3Comments

oriSomething picture oriSomething  路  3Comments

wahengchang picture wahengchang  路  3Comments

MrOrz picture MrOrz  路  3Comments