Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript Props table limitations (MDX) <Props of={Component}/> #8143

Open
sami616 opened this issue Sep 20, 2019 · 16 comments
Open

TypeScript Props table limitations (MDX) <Props of={Component}/> #8143

sami616 opened this issue Sep 20, 2019 · 16 comments

Comments

@sami616
Copy link

sami616 commented Sep 20, 2019

I'm not sure if this also the case stories written in CSF.

I've noticed a couple of limitations when trying to auto generating prop tables in typescript.

1. It looks like only Interfaces are supported, using a Type seems to break it.
2. using generics (in the case of react) seem to break it too, example below:

This works:

export const Text = ({
  padding = '0',
  margin,
}: Props) => ()

This doesn't

export const Text: React.FC<Props> = ({
  padding = '0',
  margin,
}) => ()

3. Using imported types like the example below are always inferred as any

export interface Props {
 foo: SomeImportedType['property']
}

4. Doesnt work with React.forwardRef

@shilman
Copy link
Member

shilman commented Sep 21, 2019

forwardRef is a dupe to #7933, will treat that separately. The rest are super interesting, and I hope to dig in soon.

@kfayelun
Copy link

kfayelun commented Oct 12, 2019

I'm also not getting any details for my props. Im seeing the prop name, but not type or default, even though it is defined in the component. Im using CSF, but project is gatsby so using manual install with webapck config, not the preset. Very open for it being my own config causing this, Im no webpack pro. Components are functional so no React.Component options like described above.

Was struggling with other issues, they should be fixed now, heres a repo: https://github.com/netliferesearch/netlife2019/tree/storybook_setup_stories

@shilman
Copy link
Member

shilman commented Oct 15, 2019

FYI, @mrmckeb has done some awesome detective work that's possibly related to this. The TLDR is that there may be two problems:

  1. The docgen loader may need to be pointed at your tsconfig.json using something like this:
      tsDocgenLoaderOptions: {
        tsconfigPath: path.resolve(__dirname, '../tsconfig.json'),
      },
  1. There are potential Babel plugin conflicts:
              // The Babel plugin for docgen conflicts with the TypeScript loader.
              // This limits it to JavaScript files when the TypeScript loader is enabled.
              if (options.tsDocgenLoaderOptions) {
                plugins = _plugins.filter(
                  ([plugin]) => !plugin.includes('babel-plugin-react-docgen')
                );
                overrides = [
                  {
                    test: /\.(js|jsx)$/,
                    plugins: _plugins.filter(([plugin]) =>
                      plugin.includes('babel-plugin-react-docgen')
                    ),
                  },
                ];

These changes have improved the docgen support in the CRA preset's typescript docgen settings: https://github.com/storybookjs/presets/pull/44/files

I'm not sure how to operationalize them for more general configuration yet, but wanted to record that here in case anybody wants to experiment in their own setups!

@kfayelun
Copy link

@shilman I'm not using typescript and still not seeing prop types and defaults. Should I file a separate bug?

@shilman
Copy link
Member

shilman commented Oct 15, 2019

@kfayelun Yeah, I think that's a separate issue (though it may end up being the same underlying problem). Thanks!

@mrmckeb
Copy link
Member

mrmckeb commented Oct 16, 2019

We do know that the TS and JS loaders/plugins can interfere with each other. A good base test is to ensure that you don't have both in the mix ;)

@jonathanloske
Copy link

jonathanloske commented Oct 16, 2019

Insight for point 1, 2, and 4: For us, it turned out that this is caused by how we extend Component: If we do class MyComponent extends React.Component, descriptions are omitted. If we use a named import of Component instead (class MyComponent extends Component), descriptions work! This also applies to React.FC vs FC!

We were even able to use type like this:

type CheckboxProps = {
	id: string;
}

const Checkbox: FC<CheckboxProps> = props => {
	...
}

🤔 The error with React.forwardRef might also be solvable with named imports but we don't use it here and I'm not sure how to use it.

@shilman
Copy link
Member

shilman commented Oct 16, 2019

@jonathanherdt ❤️ ❤️ ❤️ Thanks for tracking this down. Super helpful!

Re: forwardRef, there's a PR in progress #8445

@stale
Copy link

stale bot commented Nov 6, 2019

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 stale bot added the inactive label Nov 6, 2019
@stale stale bot removed the inactive label Nov 7, 2019
@shilman shilman modified the milestones: 5.2.x, 5.3.x Jan 11, 2020
shilman added a commit that referenced this issue Feb 14, 2020
@shilman
Copy link
Member

shilman commented Feb 14, 2020

Generics issue (2) repro: e3927d9

I believe this is the corresponding react-docgen issue: reactjs/react-docgen#387

shilman added a commit that referenced this issue Feb 14, 2020
@shilman
Copy link
Member

shilman commented Feb 14, 2020

Type import issue (3) repro: 4129773

Possibly fixed in reactjs/react-docgen#352

@shilman
Copy link
Member

shilman commented May 21, 2020

We've just released zero-config typescript support in 6.0-beta. Please upgrade and test it!

Thanks for your help and support getting this stable for release!

@shilman shilman removed this from the 5.3.x milestone Jul 30, 2020
@monolithed
Copy link

monolithed commented Nov 12, 2020

Possibly fixed, but ArgsTable is not rendered
Снимок экрана 2020-11-12 в 23 59 58

// icon.tsx

import React, {ComponentProps} from 'react';
import AntdIcon from '@ant-design/icons/lib/components/Icon';

type Props = ComponentProps<typeof AntdIcon>;

const Icon = React.forwardRef<HTMLSpanElement, Props>(({...props}, ref) => {
    return <AntdIcon data-test={AntdIcon.displayName} ref={ref} {...props} />;
});

export {Icon};
export type {Props as IconProps};
// icon.story.tsx

import React from 'react';
import {Story} from '@storybook/react/types-6-0';
import {MessageOutlined} from '@ant-design/icons';
import {Icon, IconProps} from '../..';

const IconDefaultTemplate: Story<IconProps> = ({...props}) => {
    return <Icon component={MessageOutlined} />;
};

const IconDefaultStory = IconDefaultTemplate.bind({});

IconDefaultStory.storyName = 'Базовый тип';

export {IconDefaultStory};
// icon.stories.mdx

import {Meta, ArgsTable, Story} from '@storybook/addon-docs/blocks';
import {Icon} from './icon';
import {IconDefaultStory} from './icon.story';

<Meta title={title} component={Icon} />

<Story name="Базовая иконка">
    {IconDefaultStory}
</Story>

<ArgsTable of={Icon} />

@shilman
Copy link
Member

shilman commented Nov 13, 2020

This seems to be due to a limitation of Storybook that by default it only runs type extraction on the user's project. So I think what's happening is:

const Icon = React.forwardRef<HTMLSpanElement, Props>(({...props}, ref) => {
    return <AntdIcon data-test={AntdIcon.displayName} ref={ref} {...props} />;
});

The argType extractor sees this code and tries to find the types for AntdIcon, but because that's defined in an external package, it's not available.

As a workaround, you can try setting the following option in .storybook/main.js:

module.exports = {
  typescript: {
    reactDocgenTypescriptOptions: {
      shouldExtractLiteralValuesFromEnum: true,
      shouldRemoveUndefinedFromOptional: true,
    },
  },
};

This removes the default propFilter which is:

    propFilter: (prop: any) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),

More info: https://storybook.js.org/docs/react/configure/typescript

This probably needs documentation @jonniebigodes

@monolithed
Copy link

monolithed commented Nov 13, 2020

@shilman, that works, although argTypes is not an optional parameter now. Thanks!
Additionally I'd to pay attention to the main problem I asked before:

import React from 'react';
import {Story} from '@storybook/react/types-6-0';
import AntButton from 'antd/lib/button';

export const argTypes = {
    href: {
        description: 'Ссылка, куда будет выполняться перенаправление',
        table: {
            type: {
                summary: 'string'
            }
        },
        control: {
            type: 'text'
        }
    }
};

export const ButtonDefaultTemplate = ({...props}) => {
    return <Button {...props}>Кнопка</Button>;
};

export const ButtonDefaultStory = ButtonDefaultTemplate.bind({});

ButtonDefaultStory.storyName = 'Базовый тип';

<Meta title="Button" component={Button} argTypes={argTypes} />

<Preview>
    <Story name="Button">
        {ButtonDefaultStory}
    </Story>
</Preview>

<ArgsTable of={Button} />

Снимок экрана 2020-11-14 в 0 41 07

How to hide all inherited properties and show controls instead?

@monolithed
Copy link

monolithed commented Dec 1, 2020

As a workaround, it's possible to create a custom table (but without controls):

import React, {ComponentProps, FunctionComponent} from 'react';
import {Table, Typography} from 'antd';

type TableProps = {
    name: string;
    dataSource: ComponentProps<typeof Table>['dataSource'];
};

const render = (html: string): JSX.Element => {
    return <div dangerouslySetInnerHTML={{__html: html}} />;
}

const CustomArgsTable: FunctionComponent<TableProps> = ({name, dataSource}): JSX.Element => (
    <div>
        <Typography.Title level={3}>{name}</Typography.Title>

        <Table dataSource={dataSource}
               columns={[
                   {
                       title: 'Property',
                       dataIndex: 'property',
                       key: 'property'
                   },
                   {
                       title: 'Description',
                       dataIndex: 'description',
                       key: 'description',
                       render
                   },
                   {
                       title: 'Type',
                       dataIndex: 'type',
                       key: 'type',
                       render
                   },
                   {
                       title: 'Value',
                       dataIndex: 'value',
                       key: 'value'
                   }
               ]}
               bordered={true}
               pagination={{hideOnSinglePage: true}}
        />
    </div>
);

export {CustomArgsTable};
import React from 'react';
import {Story} from '@storybook/react/types-6-0';

import {Button} from './button';
import {ButtonDefaultStory , argTypes} from './button.stories.tsx';
import {CustomArgsTable} from './utils.tsx';

export const dataSource = [
    {
        key: '1',
        property: 'dropdownProps',
        description: 'Props',
        type: 'Dropdown',
        value: '-'
    },

<Meta title="Button" component={Button} argTypes={argTypes} />

<Preview>
    <Story name="Button">
        {ButtonDefaultStory}
    </Story>
</Preview>

<CustomArgsTable name="Button" dataSource={dataSource} />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants