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

addon-docs not showing all props for styled-component #11933

Open
chrisahardie opened this issue Aug 12, 2020 · 26 comments
Open

addon-docs not showing all props for styled-component #11933

chrisahardie opened this issue Aug 12, 2020 · 26 comments

Comments

@chrisahardie
Copy link

Describe the bug

Please note this is for the pre-release version as styled-components were not able to be documented until this issue was resolved.

When documenting a styled-component, not all props are rendered out into the props table.

To Reproduce
Steps to reproduce the behavior:

  1. Clone the repro repo
  2. npx sb@next upgrade --prerelease
  3. yarn add babel-loader --dev
  4. update the main.js stories glob from ../src/**/*.stories.(ts|tsx|js|jsx|mdx) to ../src/**/*.stories.@(ts|tsx|js|jsx|mdx)
  5. View the button story
  6. The props table is missing the disabled and type props

Expected behavior

The props table should include all props.

Screenshots

image

Code snippets

// Button.tsx
interface Props {
  /**
   * Indicates if the button is disabled
   */
  disabled?: boolean;
  /**
   * Type of button
   */
  type: "submit" | "button" | "reset";
}

/**
 * Button component
  * @returns a button component
 */
export const Button = styled.button.attrs<Props>((props) => ({
  type: props.type
}))`
  ...
`

System:

Environment Info:

  System:
    OS: Windows 10 10.0.18363
    CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
  Binaries:
    Node: 12.16.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.4 - C:\Program Files\nodejs\yarn.CMD
    npm: 6.13.4 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 84.0.4147.105
    Edge: Spartan (44.18362.449.0)
  npmPackages:
    @storybook/addon-actions: ^6.0.2 => 6.0.2
    @storybook/addon-docs: ^6.0.2 => 6.0.2
    @storybook/addon-links: ^6.0.2 => 6.0.2
    @storybook/addons: ^6.0.2 => 6.0.2
    @storybook/preset-create-react-app: ^3.1.4 => 3.1.4
    @storybook/react: ^6.0.2 => 6.0.2
@shilman shilman added this to the 6.0.x milestone Aug 13, 2020
@shilman shilman self-assigned this Aug 13, 2020
@stefanb2
Copy link

stefanb2 commented Aug 21, 2020

I think this is the same issue I ran into with low-level styled components:

$ fgrep -e @storybook -e styled -e '"react"' package.json 
    "@storybook/addon-essentials": "^6.0.16",
    "@storybook/react": "6.0.16",
    "react": "16.13.1",
    "styled-components": "5.1.1",

Example/index.js:

import PropTypes from 'prop-types';
import styled from 'styled-components';

export const Example = styled.p`
  background-color: ${props => props.color};
  height: 100px;
  width: 100px;
`;

Example.propTypes = {
  /** background color */
  color: PropTypes.string.isRequired,
};

Example/index.stories.js:

import React from 'react';
import {Example} from '.';

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

export const Default = args => <Example {...args} />;
Default.args = {
  color: 'red',
};

image

If I wrap the component in a dummy wrapper...

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

const _Example = styled.p`
  background-color: ${props => props.color};
  height: 100px;
  width: 100px;
`;

export const Example = args => <_Example {...args} />

Example.propTypes = {
  /** background color */
  color: PropTypes.string.isRequired,
};

...then the documentation and the auto-generated controls work:
image

But of course I don't want to do that, because it adds another level in the React component browser every time I use the component. Dumping the component to the browser console could hint at the root cause: it is an object and not a function that returns react components:
image

@sdalonzo
Copy link

I've encountered the same in trying to migrate a Design System to Storybook 6.

@shilman
Copy link
Member

shilman commented Aug 26, 2020

@sdalonzo an upvote on the original issue is the best way to help get this fixed (aside from contributing a fix)

@AlexAxis
Copy link

AlexAxis commented Dec 12, 2020

@stefanb2 if you want a function that returns react components, you could do it this way:

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

const _Example = styled.p`
  background-color: ${props => props.color};
  height: 100px;
  width: 100px;
`;

export function Example({ children, ...props}) {
  return <_Example {...props}>{children}</_Example>
}

Example.propTypes = {
  /** background color */
  color: PropTypes.string.isRequired,
};

and in the file example.stories.js you use it this way:

import React from "react"
import { Example } from "../components/Example"
import { action } from "@storybook/addon-actions"

export default {
  title: "Example",
  component: Example,
}

export const BackgroundColor = (args) => (
  <Example {...args} onClick={action("Clicked!")}>
    This is a button
  </Example>
)
BackgroundColor.args = {
  color: "#aaff33",
}

@ebellumat
Copy link

Still not working =(

I tried a lot of work arounds and nothing.

I don't want to write these props on docs manually.

I'm using MDX with Styled components here.

@marciobarrios
Copy link

marciobarrios commented Apr 27, 2021

@ebellumat This is the workaround I used without touching the original component:

// Button.stories.tsx

import React from 'react'
import { Meta, Story } from '@storybook/react'

import { Button, ButtonProps } from './Button'

// This wrapper is needed to be able to generate the props table/controls automatically
export const ButtonWrapper = (props: ButtonProps) => (
  <Button {...props} />
)

ButtonWrapper.storyName = 'Button'

export default {
  title: 'Components/Button',
  component: ButtonWrapper,
} as Meta

@stefanb2
Copy link

@marciobarrios I just tested your suggestion and unfortunately it doesn't solve the issue for low-level styled components :-(

@marciobarrios
Copy link

@stefanb2 if you prepare a Codesandbox I can take a look, in my case it solved the styled-component issue auto generating props table and controls.

@stefanb2
Copy link

stefanb2 commented Apr 28, 2021

@marciobarrios see my full example code in comment #11933 (comment) above.

My current setup is:

$ fgrep -e @storybook -e styled -e '"react"' package.json 
    "@storybook/addon-essentials": "^6.0.21",
    "@storybook/react": "6.0.21",
    "react": "16.13.1",
    "styled-components": "5.2.0",

There seems to be at least one improvement: any prop listed in story args is now shown, but without documentation.

image

@alexbchr
Copy link

I was actually able to fix this issue simply by renaming my component file names to use .tsx instead of .ts. I was using the .ts extension as there was no JSX used in the file (only using Styled Components).

After this fix, all my props showed up with the component description populated with the JSDoc as well. However, the theme, as and forwardedAs props from Styled Components were shown as well. To remove them, I added this configuration in my .storybook/main.js file:

typescript: {
  reactDocgenTypescriptOptions: {
    propFilter: prop =>
      (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true) && 
      excludedProps.indexOf(prop.name) < 0,
  },
}

I hope this helps someone!

@shilman shilman removed this from the 6.2 docs milestone Jun 8, 2021
@YuCJ
Copy link

YuCJ commented Jun 16, 2021

Come from #12387 .

Is there any workaround for styled-components in JavaScript?

// Button.jsx

function BaseButton({title, onClick, className}) {
  return (
    <div className={className}>
      <h1>{ title }</h1>
      <button onClick={onClick}>Click me!</button>
    </div>
  );
}

const Button = styled(Button)`/* ... */`

Button.propTypes = {
  title: PropTypes.string,
};

Button.defaultProps = {
  title: 'default title',
};

export default Button;

I want to document the Button.jsx (which is a styled component) with Button.propTypes. And I can't wrap it with another React component. Because I need to use the Button as a selector like this:

import styled from 'styled-components'
import Button from './Button.jsx'

const App = styled.div`
  ${Button} {
   color: 'red';
  }
`

Is there any way to auto-create args table for Button.jsx?

@shilman
Copy link
Member

shilman commented Jun 16, 2021

@YuCJ you could do a workaround like:

export const BaseButton = ({ ... }) => ...;
BaseButton.propTypes = { ... };
BaseButton.defaultProps = { ... };

export const Button = styled(BaseButton)...;
Button.propTypes = ...
Button.defaultProps = ...

Then in your stories file:

import { Button, BaseButton } from './Button';

export default {
  title: ...
  component: BaseButton,
  ...
}

export const Story1 = () => <Button {...} />;

@robertkirsz
Copy link

What worked for me, was removing Story<Props>, so I replaced this:

export const MyStory: Story<Props> = args => <MyComponent {...args} />

With this:

export const MyStory = (args: Props) => <MyComponent {...args} />

I now get the full props table. And MyComponent is a pure styled-component, no need for React wrappers.

@protoEvangelion
Copy link

protoEvangelion commented Sep 16, 2021

@shilman I've opened a repo which demonstrates that none of the above workarounds work for this very simple typescript + styled components wrapper:

export const StyledButton = styled(Button)`
  background: red;
`;

Workaround 1: #11933 (comment)
Workaround 2: #11933 (comment)

  • Doesn't work because my files are already .tsx

Workaround 3: #11933 (comment)

  • This is the same exact workaround as workaround 1 which is to simply create a wrapper and manually specify the types

image


It's one issue that the types are not automatically deduced when there is a styled components wrapper.

But it is a totally different issue that even when manually specifying types like this that it doesn't display them:

export default {
  title: "Example/ButtonWorkAroundOne",
  component: StyledButton,
} as Meta<ButtonProps>;

export const StyledButtonWrapper = (args: ButtonProps) => (
  <StyledButton {...args} label="button" />
);

And one more issue to top it off, is that the component src is shown with the styled wrapper rather than the src of the story:

image

@soulfresh
Copy link

soulfresh commented Sep 20, 2021

@rdotg I was able to resolve the <Styled(Button) .../> naming issue by adding babel-plugin-styled-components to my babel config (not sure if you've tried that yet). However, I'm still experiencing the other issues you mentioned even after trying all of the work arounds.[Edit] I was able to get the "wrapped component" workaround by moving it into the component file (as opposed to the story file) 🤦‍♂️

@DavidHenri008
Copy link

Any news on a solution from Storybook?

@stale
Copy link

stale bot commented Jan 9, 2022

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 Jan 9, 2022
@DavidHenri008
Copy link

Ping to maintain the issue

@gregg-cbs
Copy link

This is still an issue in "@storybook/react": "^6.4.17"

@thayllachristine
Copy link

any solution for .mdx files?

@rickyzhangca
Copy link

Ref

thanks, helped me a lot, easy to customize

@soulfresh
Copy link

The best work arounds I've found for this are...

CSF + Typescript:

// MyComponent.tsx
interface MyComponentProps { ... }
export const MyComponent = (props: MyComponentProps) => { ... }

/** 
  * Docs for MyComponent go here in order to show up in Storybook.
  * It's a bummer because it breaks my IDE docs.
  */
export const MyComponentStorybook = (props: MyComponentProps) => <MyComponent {...props} />


// MyComponent.stories.tsx
import { MyComponentStorybook as MyComponent, MyComponentProps } from './MyComponent'

export default {
  component: MyComponent,
  // ArgTypes can be customized
  argTypes: { ... }
  ...
}

export const Template = (props: MyComponentProps) => <MyComponent {...props} />

MDX + Typescript:

// MyComponent.tsx
interface MyComponentProps { ... }

/** 
  * Docs for MyComponent go here.
  * This fixes my IDE docs but it breaks ArgType customizations :'(
  */
export const MyComponent = (props: MyComponentProps) => { ... }

export const MyComponentStorybook = (props: MyComponentProps) => <MyComponent {...props} />


// MyComponent.stories.mdx
import { MyComponentStorybook, MyComponent, MyComponentProps } from './MyComponent'

<Meta
  title={meta.title}
  component={MyComponent}
  // ArgType customizations don't work
/>

# MyComponent
<Description of={MyComponent} />

<Canvas>
  <Story name="MyComponent">
    <MyComponent />
  </Story>
</Canvas>

<ArgsTable of={MyComponentStorybook} />

@zackphilipps
Copy link

👋 Hello there! I've written a blog post that references this issue! You can view the applicable excerpt here: Styled Components: If using JS and PropTypes, ArgsTable won’t pull props

GitHub discussion: #18915

✌️

@The-Code-Monkey
Copy link
Contributor

I have tried all of the above, work arounds an none of them work, I have styled components and also am pulling types from a third party lib and my props come through but the third party ones dont? any reason for this?

@shilman shilman removed the P3 label Oct 18, 2022
@bmsuseluda
Copy link

bmsuseluda commented May 4, 2023

I have the same issue with storybook 7.0.6.

My workaround is the following:

import { CSSProp, DefaultTheme } from "styled-components";

export interface StyledComponentsBaseProps {
  as?: keyof JSX.IntrinsicElements;
  css?: CSSProp<DefaultTheme>;
}

export interface Props
  extends StyledComponentsBaseProps,
    HTMLAttributes<HTMLDivElement> {
  color?: "bright" | "dark";
}

export const Component: React.FC<Props> = styled.div<Props>`
  ${({ color = "black" }) => css`
    color: ${color === "bright"
      ? 'white'
      : 'black'};
  `}}
`;

The downside are:

  • you have to type the default props for a styled component and the corresponding html tag
  • if you overwrite the HTML tag with the as prop you don't have the special props of the specific tag you choose, e.g. "href" tag for anchor

@oceanicsdotio
Copy link

oceanicsdotio commented Dec 10, 2023

Found a (TSX) way that suits my use case.

Import base and styled versions, and use the base version in the Meta and the styled version in the Story.

One caveat that threw me for a loop: only picks up comments in /** something */ format.

import React from 'react';
import {StoryFn, Meta} from '@storybook/react';
import StyledComponent, {Component} from './Component';
import type {ComponentType} from './Component';
 
export default {
  component: Component
} as Meta
 
const Template: StoryFn<ComponentType> = (args) => <StyledComponent {...args} />;
  
export const Default = Template.bind({});

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