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

Webpack5: Typescript argTypes not auto-generated #14118

Closed
Titou325 opened this issue Mar 3, 2021 · 33 comments
Closed

Webpack5: Typescript argTypes not auto-generated #14118

Titou325 opened this issue Mar 3, 2021 · 33 comments

Comments

@Titou325
Copy link

Titou325 commented Mar 3, 2021

Describe the bug
It seems that typescript typings for react components do not get picked up correctly when using Storybook 6.2 beta

To Reproduce
See attached snippets

Expected behavior
Storybook should pick up the typescript typings (enums, ...) correctly and not show No inputs found for this component.

Screenshots
image

Code snippets
Sample component

import Icon from "../Icon"
import React, { useState } from "react"
import TextSkeleton from "../TextSkeleton"
import * as styles from "./accordion.module.scss"
import { IElementProps } from "../typings/props"
import { tabFocusEnter } from "../utilities/tabFocusEnter"

const Accordion: React.FC = ({ children }) => {
  return <div className={styles.accordion}>{children}</div>
}

interface IAccordionItemProps extends IElementProps {
  /** Title of the accordion tab */
  title?: string;
  /** Whether or not the accordion tab should be open by default */
  defaultOpen?: boolean;
}

const AccordionItem: React.FC<IAccordionItemProps> = ({ children, defaultOpen = false, title = "Dropdown", skeleton = false, disabled = false }) => {
  const [isOpen, setIsOpen] = useState(defaultOpen || false)

  console.log(styles)

  const toggle = () => {
    setIsOpen(!isOpen)
  }

  return <div
    className={[
      styles.accordionItem,
      styles[
      "accordionItem_state_" + (isOpen ? "open" : "closed")
      ],
      styles[
      "accordionItem_ui_" +
      (disabled ? "disabled" : "enabled")
      ]
    ].join(" ")}
  >
    <div
      onClick={toggle}
      tabIndex={disabled ? undefined : 0}
      onKeyDown={tabFocusEnter(toggle)}
      role="button"
      className={styles.accordionItemTitle}
    >
      <span
        className={[
          styles.accordionItemTitleIcon,
          styles.accordionItemTitleChild
        ].join(" ")}
      >
        <Icon name="Chevron down" size="16" />
      </span>
      <span
        className={[
          styles.accordionItemTitleText,
          styles.accordionItemTitleChild
        ].join(" ")}
      >
        {skeleton ? (
          <TextSkeleton />
        ) : (
          title
        )}
      </span>
    </div>
    <div className={styles.accordionItemContent}>
      {skeleton ? (
        <TextSkeleton lines={5} />
      ) : (
        children
      )}
    </div>
  </div>
}

export { Accordion, AccordionItem }

External typings

import React from "react"

interface IElementProps extends React.HTMLAttributes<Element> {
    /** Whether or not to display a skeleton instead of the accordion contents */
    skeleton?: boolean;
    /** Whether or not the component should be disabled */
    disabled?: boolean;
}

Storybook main config

const sassJson = require('node-sass-json-functions')

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

const cssLoaderRule = {
  loader: 'css-loader',
  options: {
    sourceMap: true,
    modules: {
      namedExport: true,
      localIdentName: "[name]--[local]--[hash:base64:5]",
      exportLocalsConvention: "dashesOnly",
      exportOnlyLocals: false
    }
  }
}

const postCssLoaderRule = {
  loader: 'postcss-loader',
  options: {
    execute: false,
    sourceMap: true
  }
}

const sassLoaderRule = {
  "loader": 'sass-loader',
  "options": {
    "sassOptions": {
      "functions": {
        ...sassJson
      },
      "file": null,
      "data": null,
      "includePaths": [],
      "indentedSyntax": false,
      "indentType": "space",
      "indentWidth": 2,
      "linefeed": "lf",
      "omitSourceMapUrl": false,
      "outFile": null,
      "precision": 5,
      "sourceComments": false,
      "sourceMapContents": false,
      "sourceMapEmbed": false
    }
  }
}

const sassRule = {
  test: /\.s(a|c)ss$/,
  use: [
    MiniCssExtractPlugin.loader,
    cssLoaderRule,
    postCssLoaderRule,
    sassLoaderRule
  ]
}

const sassRuleModules = {
  test: /\.module\.s(a|c)ss$/,
  use: [
    {
      loader: MiniCssExtractPlugin.loader,
      options: {
        modules: {
          namedExport: true
        }
      }
    },
    cssLoaderRule,
    postCssLoaderRule,
    sassLoaderRule
  ]
}

const rules = {
  oneOf: [
    sassRuleModules,
    sassRule
  ]
}

module.exports = {
  "stories": [
    "../src/**/*stories.mdx",
    "../src/**/*stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    "@storybook/addon-essentials",
    "@storybook/addon-a11y",
    "@storybook/addon-links",
    "@etchteam/storybook-addon-status/register"
  ],
  typescript: {
    reactDocgen: 'react-docgen-typescript'
  },
  webpackFinal: async (config) => {
    config.plugins.push(new MiniCssExtractPlugin())
    config.module.rules.push(rules)

    config.resolve.fallback = {
      ...config.resolve.fallback
    }

    return config
  },
  core: {
    builder: 'webpack5',
  }
}

System

Environment Info:

  System:
    OS: Windows 10 10.0.19041
    CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
  Binaries:
    Node: 12.3.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.10 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 6.14.9 - ~\AppData\Roaming\npm\npm.CMD
  Browsers:
    Chrome: 88.0.4324.190
    Edge: Spartan (44.19041.423.0), Chromium (88.0.705.81)
  npmPackages:
    @storybook/addon-actions: ^6.2.0-beta.6 => 6.2.0-beta.6
    @storybook/addon-essentials: ^6.2.0-beta.6 => 6.2.0-beta.6
    @storybook/addon-links: ^6.2.0-beta.6 => 6.2.0-beta.6
    @storybook/builder-webpack5: ^6.2.0-beta.6 => 6.2.0-beta.6
    @storybook/react: ^6.2.0-beta.6 => 6.2.0-beta.6
    @storybook/theming: ^6.2.0-beta.6 => 6.2.0-beta.6

Additional context
We have in the same project a Gatsby 3 instance using the components that are shown in Storybook, hence our switch to Sb^6.2 to switch the whole codebase to Webpack 5.

It seems that some typings are getting picked up, but not correctly as shown in the screenshot below where cardRatio is an enum and shown as a text field.

image

Thanks a lot for your help & time

┆Issue is synchronized with this Asana task by Unito

@shilman
Copy link
Member

shilman commented Mar 3, 2021

Do you have a repro repo you can share?

@Titou325
Copy link
Author

Titou325 commented Mar 4, 2021

Thanks for your reply,

I have set up a sample repro at https://github.com/Titou325/storybook-14118
I have stripped most of the useless files to keep it simple, but have left the dependencies in case it would come from a conflict there.

Do not hesitate to reach out if I can be of any help,

Have a nice day

@shilman
Copy link
Member

shilman commented Mar 4, 2021

Thanks so much for the repro! I took a look. Some initial notes:

  • react-docgen is sort of working
  • react-docgen-typescript is not processing the files

I ran DEBUG=docgen:include,docgen:exclude yarn storybook and AFAICT the .tsx files are not there.

Will continue digging.

@Titou325
Copy link
Author

Titou325 commented Mar 6, 2021

Hi! Hope you are doing well,

I have been investigating a bit on my end, and the problem seems to occur before the typescript files are being handed to react-docgen-typescript-plugin so there might be an error with the babel loader for typescript files.

I have tried to initialize a very simple Storybook w/ Webpack 5 repository and can't get anything to work, here is the link to the repro: https://github.com/Titou325/storybook-14118-2

I have run a very simple npx sb@next init --builder webpack5 --type react command and put a single example TS component.
I have not edited any dependencies, and have only added react,react-dom@^17.0.1 to get the components running.

I would consider this to be a blocker for the Webpack 5 release given that the docs still state that Typescript should be supported out of the box. I will try to see if I can narrow it further down, but I am far past my expertise domain here.

Again, let me know if there is anything that I can do to help you in this regard.

Have a nice weekend

@shilman
Copy link
Member

shilman commented Mar 10, 2021

Re: your new repo, there is no tsconfig.json. Storybook is not a scaffolder: it's meant to initialize itself in an existing app/design system.

I'm still stuck on your first repo, will try to spend more time on that today. Thanks for setting up these repros and for your patience!

@Titou325
Copy link
Author

Titou325 commented Mar 10, 2021

Hello!

Thanks for your reply. I definitely forgot about the tsconfig.json, I have added it and pushed it however SB is still not picking any typings. I am going to try to investigate to see if I missed something else. (TS is working as intended in the environment although not using a bootstrapping library such as cra)

Thanks again, it is a pleasure to be of any help. Have a nice day!

@skovy
Copy link
Contributor

skovy commented Mar 15, 2021

We're also experiencing this issue with the following versions in a React TypeScript project (we also tried 6.2.0-alpha.35 and 6.2.0-rc.0):

    "@storybook/addon-a11y": "6.2.0-beta.13",
    "@storybook/addon-actions": "6.2.0-beta.13",
    "@storybook/addon-controls": "6.2.0-beta.13",
    "@storybook/addon-docs": "6.2.0-beta.13",
    "@storybook/addons": "6.2.0-beta.13",
    "@storybook/builder-webpack5": "6.2.0-beta.13",
    "@storybook/react": "6.2.0-beta.13",

This is resulting in a runtime error for some stories: Cannot convert undefined or null to object. This happens when we explicitly define a control:

    type: {
      control: 'radio',
    },

Previously, my understanding is that react-docgen-typescript would infer the options from the types, and that would get merged in with this explicit override. However, now there are no inferred options, it's undefined which is throwing this error in the RadioControl when it calls the helpers Object.entires(options):

const entry = Object.entries(options).find(([_key, val]) => val === value);
.

Object.entries(undefined)
// Uncaught TypeError: Cannot convert undefined or null to object

I was able to repro by copying the react-ts example and changing it to webpack 5 (instead of 4).

Running DEBUG=docgen:* yarn storybook (as you mentioned, and in the docs: https://github.com/hipstersmoothie/react-docgen-typescript-plugin) includes this output:

  docgen:exclude Ignoring un-built module: src/button.tsx +0ms

However, running on webpack 4 produces:

  docgen:include src/button.tsx +0ms

This specific error (Ignoring un-built module) is produced on this line:

https://github.com/hipstersmoothie/react-docgen-typescript-plugin/blob/abe4c74f104212b1d32e7698eab3301fb2b8dbb6/src/plugin.ts#L209

And this implies built is falsy:

https://github.com/hipstersmoothie/react-docgen-typescript-plugin/blob/abe4c74f104212b1d32e7698eab3301fb2b8dbb6/src/plugin.ts#L208

I'm not sure what the built flag indicates, but could be related to breaking changes between webpack v4 and v5?

The only short-term fix we are aware of is to explicitly comment out control: "radio" to prevent the runtime errors and lose all autogenerated docs, options, etc.?

If helpful, I can create a repro repo, but I believe converting react-ts in the examples to webpack 5 should reproduce this same issue.

Let me know if there are any more details that would be helpful in debugging 👍

@shilman shilman added the P0 label Mar 15, 2021
@shilman shilman added this to the 6.2 blocking milestone Mar 16, 2021
@shilman shilman self-assigned this Mar 16, 2021
@shilman
Copy link
Member

shilman commented Mar 19, 2021

@skovy thanks for the debugging. i'm able to repro on react-ts and am trying to understand what's going on.

From the webpack docs on module.built:

  "built": true, // Indicates that the module went through [Loaders](/concepts/loaders), Parsing, and Code Generation

@shilman
Copy link
Member

shilman commented Mar 19, 2021

@hipstersmoothie I took a look at this with @ndelangen and it looks like react-docgen-typescript-plugin needs to be updated to webpack5.

We looked at the built field and it is apparently not present in webpack5. We modified the plugin code in node_modules to use this check instead:

                    if (module.built === false) {
                        debugExclude(`Ignoring un-built module: ${module.userRequest}`);
                        return;
                    }

After that hack, the .tsx files are getting processed, but the source modification code (which looks incredibly brittle) breaks in webpack5. I asked @sokra what's the proper way to do it and he replied:

Instead of modifying the source code directly: Add a Dependency to the Module via module.addDependency. Register a DependencyTemplate for that Dependency class (compilation.dependencyTemplates.set(...)). In the DependencyTemplate you can add the code block to the source. Note if you want that modules correctly invalidate and cache you need to add updateHash to your Dependency and hash the type info (because that might change depending on outside factors (other modules). This should happen during building of modules -> compilation.hooks.buildModule. Dependencies are cached with the Module. The result of the DependencyTemplate is cached when the hash is equal.

I'm still trying to decode this, but it seems like we can add these Dependencies in the buildModule step and then construct the tsProgram in the seal step and update the dependencies ... somehow.

@mrtnbroder
Copy link

@shilman shouldn't this issue get the label label:webpack5? We are also running into this issue where the typescript docs are not being generated.

@kelly-tock
Copy link

Same here. using webpack 5 in project but webpack 4 in storybook

@danantal
Copy link

Also facing this issue... I guess no workaround is available except creating argTypes manually?

@shilman
Copy link
Member

shilman commented May 14, 2021

@danantal we should have a fix for react-docgen-typescript-plugin within a week, so you're probably better off just waiting for that 😄

@shilman
Copy link
Member

shilman commented May 20, 2021

Crikey!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.3.0-alpha.35 containing PR #14991 that references this issue. Upgrade today to the @next NPM tag to try it out!

npx sb upgrade --prerelease

Closing this issue. Please re-open if you think there's still more to do.

@shilman shilman closed this as completed May 20, 2021
@shilman
Copy link
Member

shilman commented May 20, 2021

ZOMG!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.3.0-alpha.36 containing PR #14991 that references this issue. Upgrade today to the @next NPM tag to try it out!

npx sb upgrade --prerelease

@petr001
Copy link

petr001 commented May 26, 2021

I've tested newest version (6.3.0-beta.1) and the issue is still there. I've upgraded with the command npx sb upgrade --prerelease. Should I do something more?

@mrtnbroder
Copy link

I’m with @petr001. Issue still exists.

@shilman
Copy link
Member

shilman commented May 26, 2021

@petr001 @mrtnbroder Can you please create a reproduction by running npx sb@next repro, following the instructions, and linking it here? It's been working great for me.

@petr001
Copy link

petr001 commented Jul 20, 2021

The command is not working for me.

But my steps to reproduce:

Installed storybook version was 6.3.4. I've tried also 6.4.0-alpha.18 (with the same result).

Any hint why it's not working?

@petr001
Copy link

petr001 commented Jul 20, 2021

I've found it - it's because of this bug: styleguidist/react-docgen-typescript#323

@ShuPink
Copy link

ShuPink commented Jan 10, 2022

I also had this issue using 6.4.9, thanks to @petr001 managed to get the following workaround which is to change the Component export from default exports to named export

@kyle-copeland
Copy link

@ShuPink this addressed the auto-generation for me as well. Thanks for posting this! I'd like to avoid exporting our components as named exports. Does anyone have an insight around why default exports might now be generating controls?

@Hideman85
Copy link

Storybook 6.4.13 for React started still having empty doc only the code got parsed...

@oreqizer
Copy link

Still present in v6.4.19, even if I do import * as React from "react";. only fixed if I export a component not as default, but as named

@shiranZe
Copy link

@oreqizer Hey, did you manage to find a solution? except from named export

@oreqizer
Copy link

@oreqizer Hey, did you manage to find a solution? except from named export

Nope

@AbdallahAbis
Copy link

Any workaround on this guys?

@thereis
Copy link

thereis commented May 31, 2022

I am still having this issue. Does anyone fixed it?

@oreqizer
Copy link

oreqizer commented May 31, 2022

Any workaround on this guys?

just by also exporting a named component, and using that in stories

// Export named for Storybook
// 👉 https://github.com/storybookjs/storybook/issues/14118
export function Component () {
  // ...
}

export default Component;

then in your story:

import { Component } from "components/Component";

@dmackerman
Copy link

Does this issue affect non-TS repos as well? I'm seeing basically the same behavior in a non-TS codebase.

@AndyOGo
Copy link

AndyOGo commented Aug 23, 2022

Unfortunately that is still broken for my case.
The correct types show up in docs addon, but each control only shows "Set Object" control, instead of the proper control for the relating type.

@shilman @Titou325 please reopen it.

@shilman
Copy link
Member

shilman commented Aug 23, 2022

@AndyOGo Got a reproduction you can share?

@tonalnik
Copy link

In my case, the problem was that the component being tested was different from the file name. Once I changed the name, Storybook was able to detect all types.

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