storybookjs / storybook

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

Docs tab shows 'no code available' when using CSF format #8104

Closed mattwaler closed 4 years ago

mattwaler commented 5 years ago

Describe the bug

When composing a story using new CSF format, all stories show no code available.

To Reproduce Steps to reproduce the behavior:

  1. Compose story
  2. Start storybook
  3. Go to Docs tab

Expected behavior

Like in the screenshots and docs, code previews should be available to view and copy.

Screenshots

browser code button

System:

Environment Info:

  System:
    OS: macOS 10.14.6
    CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
  Binaries:
    Node: 12.4.0 - ~/.nvm/versions/node/v12.4.0/bin/node
    Yarn: 1.17.3 - /usr/local/bin/yarn
    npm: 6.9.0 - ~/.nvm/versions/node/v12.4.0/bin/npm
  Browsers:
    Chrome: 76.0.3809.132
    Firefox: 68.0.2
    Safari: 12.1.2
  npmPackages:
    @storybook/addon-actions: ^5.2.0 => 5.2.0 
    @storybook/addon-docs: ^5.2.0 => 5.2.0 
    @storybook/addon-links: ^5.2.0 => 5.2.0 
    @storybook/addon-notes: ^5.2.0 => 5.2.0 
    @storybook/addons: ^5.2.0 => 5.2.0 
    @storybook/react: ^5.2.0 => 5.2.0 
shilman commented 5 years ago

Can you share your preset and config files?

mattwaler commented 5 years ago
// addons.js
import '@storybook/addon-actions/register'
import '@storybook/addon-links/register'

// config.js
import { configure } from '@storybook/react'
configure(require.context('../src/stories', true, /\.stories\.(js|mdx)$/), module)

// presets.js
module.exports = [
  {
    name: '@storybook/addon-docs/react/preset',
    options: {
      configureJSX: true,
      babelOptions: {},
      sourceLoaderOptions: null,
    },
  },
]
goszczynskip commented 5 years ago

I have very similar situation but with typescript configuration. Everything is working fine except code part.

I'm not using presets. My stories are placed in src/components directory.

My webpack.config.js:

const path = require("path");

module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    use: [
      {
        loader: require.resolve('babel-loader'),
        options: {
          presets: [['react-app', { flow: false, typescript: true }]]
        }
      },
      {
        loader: require.resolve('react-docgen-typescript-loader')
      }
    ]
  });

  config.module.rules.push({
    test: /\.(stories|story)\.[tj]sx?$/,
    loader: require.resolve('@storybook/source-loader'),
    include: [path.resolve(__dirname, '../src/components')],
    enforce: 'pre',
    options: {
      injectParameters: true,
      inspectLocalDependencies: false,
      inspectDependencies: false
    }
  });

  config.resolve.extensions.push('.ts', '.tsx');

  return config;
};
karthikbalajikb commented 5 years ago

facing the same issue

riandy-dimas commented 5 years ago

I do have the same issue using typescript preset, ended up in moving to write in MDX

goszczynskip commented 5 years ago

I've found bug in my config. I changed test: /\.(stories|story)\.[tj]sx?$/, in source-loader config to test: /\.[tj]sx?$/. After that source code appears in stories.

~Also I think docs are wrong. Source-loader test regexp should be set to match sources and stories instead of stories only.~

EDIT: source-loader shouldn't be set for something else than stories. In my case regexp wasn't matching stories files.

shilman commented 5 years ago

@mattwaler sorry for the slow reply. this line from your preset is disabling the source loader, so there's no code to show:

sourceLoaderOptions: null,

shilman commented 5 years ago

@goszczynskip i don't think so. source-loader is about loading the source for stories only. if you set it to load the source for your components it will slow down your builds etc. i suspect there's some other problem with your setup.

goszczynskip commented 5 years ago

@shilman You are right. There was other problem. I've just used regexp with starting dot but my stories are like: story.tsx.

My working configuration with Typescript, MDX and sources. Maybe someone wants to copy it.

const path = require('path');
const createCompiler = require('@storybook/addon-docs/mdx-compiler-plugin');

const docgenLoader = {
  loader: require.resolve('react-docgen-typescript-loader'),
  options: {
    propFilter: prop => {
      // Filter out props from styled-components
      if (prop.name === 'as' || prop.name === 'ref' || prop.name === 'theme') {
        return false;
      }

      if (prop.parent == null) {
        return true;
      }

      // Filter out props which type definition is placed in react package
      return prop.parent.fileName.indexOf('node_modules/@types/react') < 0;
    }
  }
};

const babelLoader = {
  loader: require.resolve('babel-loader'),
  options: {
    presets: [['react-app', { flow: false, typescript: true }]]
  }
};

const mdxLoader = {
  loader: '@mdx-js/loader',
  options: {
    compilers: [createCompiler({})]
  }
};

module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.(mdx)$/,
    use: [babelLoader, docgenLoader, mdxLoader]
  });

  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    use: [babelLoader, docgenLoader]
  });

  config.module.rules.push({
    test: /(stories|story)\.[tj]sx?$/,
    loader: require.resolve('@storybook/source-loader'),
    include: [path.resolve(__dirname, '../src/components')],
    enforce: 'pre',
    options: {
      injectParameters: true,
      inspectLocalDependencies: false,
      inspectDependencies: false
    }
  });

  config.resolve.extensions.push('.ts', '.tsx');

  return config;
};
larswittenberg commented 5 years ago

@mattwaler sorry for the slow reply. this line from your preset is disabling the source loader, so there's no code to show:

sourceLoaderOptions: null,

thx. this works for me.

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!

Elijen commented 4 years ago

Same problem here. We use a custom Webpack config for TypeScript support in stories.

module.exports = ({ config }) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    use: "babel-loader"
  });
  config.resolve.extensions.push(".ts", ".tsx");
  return config;
};
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!

jeffryang24 commented 4 years ago

no_code_available

Still got no code available while using CSF format (Docs mode). But, when I switched to Canvas mode, the story source existed.

canvas_mode

webpack.config.js

// Core(s)
const path = require('path');
const createMDXCompiler = require('@storybook/addon-docs/mdx-compiler-plugin');

// Summary
module.exports = async ({ config }) => {
  config.module.rules.push(
    {
      test: /\.stories\.mdx$/,
      use: [
        {
          loader: 'babel-loader',
          // May or may not need this line depending on your app's setup
          options: {
            plugins: ['@babel/plugin-transform-react-jsx']
          }
        },
        {
          loader: '@mdx-js/loader',
          options: {
            compilers: [createMDXCompiler({})]
          }
        }
      ]
    },
    {
      test: /\.stories\.tsx?$/,
      use: [
        {
          loader: require.resolve('@storybook/source-loader'),
          options: {
            parser: 'typescript',
            prettierConfig: {
              arrowParens: 'avoid',
              printWidth: 80,
              tabWidth: 2,
              bracketSpacing: true,
              trailingComma: 'none',
              singleQuote: true,
              semi: true,
              jsxBracketSameLine: false
            }
          }
        }
      ],
      enforce: 'pre',
      include: path.resolve(__dirname, '../src')
    },
    {
      test: /\.tsx?$/,
      use: [
        {
          loader: require.resolve('babel-loader'),
          options: {
            presets: [
              [
                require.resolve('babel-preset-react-app'),
                { flow: false, typescript: true }
              ]
            ]
          }
        },
        require.resolve('react-docgen-typescript-loader')
      ]
    },
    {
      test: /\.s[ac]ss$/,
      use: ['style-loader', 'css-loader', 'sass-loader'],
      include: path.resolve(__dirname, '../')
    }
  );

  config.resolve.extensions.push('.ts', '.tsx');

  return config;
};

button.tsx

import React, { ReactNode, FunctionComponentElement } from 'react';

export type Props = {
  /**
   * Children
   */
  children?: ReactNode;
};

export function Button({ children }: Props): FunctionComponentElement<Props> {
  return <button type="button">{children}</button>;
}

export default Button;

button.stories.tsx

import React, { FunctionComponent } from 'react';
import { text } from '@storybook/addon-knobs';

import { Button } from './button';

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

export const withText: FunctionComponent = () => <Button>Hello Button</Button>;

export const withEmoji: FunctionComponent = () => (
  <Button>
    <span role="img" aria-label="so cool">
      😀 😎 👍 💯
    </span>
  </Button>
);

export const withKnobs: FunctionComponent = () => (
  <Button>{text('children', 'Test Children')}</Button>
);

:bowing_man:

shilman commented 4 years ago

@jeffryang24 your webpack config adds source-loader, but addon-docs also adds source-loader. I'd suggest removing yours, or disabling the one in addon-docs per https://github.com/storybookjs/storybook/blob/next/addons/docs/README.md#preset-options

jeffryang24 commented 4 years ago

@jeffryang24 your webpack config adds source-loader, but addon-docs also adds source-loader. I'd suggest removing yours, or disabling the one in addon-docs per https://github.com/storybookjs/storybook/blob/next/addons/docs/README.md#preset-options

I see... Will try to disable one of them. Will report the result back, probably this night (GMT+7)... :bowing_man:

patrick-radulian commented 4 years ago

I have a running storybook installation as per instructions here: https://gist.github.com/shilman/bc9cbedb2a7efb5ec6710337cbd20c0c

Currently I also see no code preview in the stories when using CSF - it only works properly when using MDX instead.

shilman commented 4 years ago

@patrick-radulian I have updated the gist, specifically this part, to reflect recent changes in addon-docs:

  {
    name: "@storybook/addon-docs/preset",
    options: {
      configureJSX: true,
    }
  }

I also tested it with a clean setup & verified that it's working

jeffryang24 commented 4 years ago

@jeffryang24 your webpack config adds source-loader, but addon-docs also adds source-loader. I'd suggest removing yours, or disabling the one in addon-docs per https://github.com/storybookjs/storybook/blob/next/addons/docs/README.md#preset-options

Hmm... Actually, I don't use the default preset, @storybook/addon-docs/preset and I don't have presets.js. I follow the manual installation from the docs (before this new doc with main.js :laughing:). I will try update the storybook to v5.2.8

jeffryang24 commented 4 years ago

@jeffryang24 your webpack config adds source-loader, but addon-docs also adds source-loader. I'd suggest removing yours, or disabling the one in addon-docs per https://github.com/storybookjs/storybook/blob/next/addons/docs/README.md#preset-options

Here's my full config: https://gist.github.com/jeffryang24/2147d8dbed6189ba6cc9a022ec79ac11 :bowing_man:. Still got no luck... Oh, yes. My project is a monorepo and has only one tsconfig.json in the root dir.... Does it cause this issue?

jeffryang24 commented 4 years ago

no_code_available

Still got no code available while using CSF format (Docs mode). But, when I switched to Canvas mode, the story source existed.

canvas_mode

webpack.config.js

// Core(s)
const path = require('path');
const createMDXCompiler = require('@storybook/addon-docs/mdx-compiler-plugin');

// Summary
module.exports = async ({ config }) => {
  config.module.rules.push(
    {
      test: /\.stories\.mdx$/,
      use: [
        {
          loader: 'babel-loader',
          // May or may not need this line depending on your app's setup
          options: {
            plugins: ['@babel/plugin-transform-react-jsx']
          }
        },
        {
          loader: '@mdx-js/loader',
          options: {
            compilers: [createMDXCompiler({})]
          }
        }
      ]
    },
    {
      test: /\.stories\.tsx?$/,
      use: [
        {
          loader: require.resolve('@storybook/source-loader'),
          options: {
            parser: 'typescript',
            prettierConfig: {
              arrowParens: 'avoid',
              printWidth: 80,
              tabWidth: 2,
              bracketSpacing: true,
              trailingComma: 'none',
              singleQuote: true,
              semi: true,
              jsxBracketSameLine: false
            }
          }
        }
      ],
      enforce: 'pre',
      include: path.resolve(__dirname, '../src')
    },
    {
      test: /\.tsx?$/,
      use: [
        {
          loader: require.resolve('babel-loader'),
          options: {
            presets: [
              [
                require.resolve('babel-preset-react-app'),
                { flow: false, typescript: true }
              ]
            ]
          }
        },
        require.resolve('react-docgen-typescript-loader')
      ]
    },
    {
      test: /\.s[ac]ss$/,
      use: ['style-loader', 'css-loader', 'sass-loader'],
      include: path.resolve(__dirname, '../')
    }
  );

  config.resolve.extensions.push('.ts', '.tsx');

  return config;
};

button.tsx

import React, { ReactNode, FunctionComponentElement } from 'react';

export type Props = {
  /**
   * Children
   */
  children?: ReactNode;
};

export function Button({ children }: Props): FunctionComponentElement<Props> {
  return <button type="button">{children}</button>;
}

export default Button;

button.stories.tsx

import React, { FunctionComponent } from 'react';
import { text } from '@storybook/addon-knobs';

import { Button } from './button';

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

export const withText: FunctionComponent = () => <Button>Hello Button</Button>;

export const withEmoji: FunctionComponent = () => (
  <Button>
    <span role="img" aria-label="so cool">
      😀 😎 👍 💯
    </span>
  </Button>
);

export const withKnobs: FunctionComponent = () => (
  <Button>{text('children', 'Test Children')}</Button>
);

cc: @shilman Could be #7829? Since I used typescript for this project. https://gist.github.com/jeffryang24/2147d8dbed6189ba6cc9a022ec79ac11 :bowing_man:

patrick-radulian commented 4 years ago

@shilman Thanks, you are right - I've just commented out a different addon (addon-storysource) and kept only docs running. I do in fact see the code preview now, but with a (rather minor) issue:

image

The bigger issue I've run into though, is the warnings I receive when storybook starts up / refreshes:

image

With Button.stories.tsx looking like this:

import React from "react";
import { Button } from "./Button";

export default {
    title: "Design System|Atoms/Button",
    component: Button
}

export const Default = () => <Button>Default button</Button>;

export const Positive = () => <Button type="positive">Positive button</Button>;

export const Danger = () => <Button type="danger">Danger button</Button>;
jeffryang24 commented 4 years ago

@jeffryang24 your webpack config adds source-loader, but addon-docs also adds source-loader. I'd suggest removing yours, or disabling the one in addon-docs per https://github.com/storybookjs/storybook/blob/next/addons/docs/README.md#preset-options

probably_the_culprit

Already found the culprit. I just made another sandbox for reproducing the issue, and finally found the root problem. When I set the parser option for @storybook/source-loader, the "No code available" issue appeared, but when I commented the parser option, the "Show Code" panel appeared and it rendered the source code perfectly. I don't know why I can't set the parser to typescript, probably #7829?

after_removing_parser_option

Here's the repo for reproducing the issue: https://github.com/jeffryang24/sandbox/tree/master/javascript-typescript/storybook-addon-docs-repro

Vanuan commented 4 years ago

I have renamed stories.tsx to Component.stories.tsx and it worked.

It looks like the preset ignores values provided from configure/require.context in config.js. Or maybe it uses heuristics

Vanuan commented 4 years ago
// config.js
import { addParameters, configure } from '@storybook/react';

addParameters({
  options: {
    storySort: (a, b) => (a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id)),
  },
});

configure(
  [
    requireContext('../src', true, /stories\.tsx$/),
  ],
  module
);

// main.js
module.exports = {
  presets: [
    '@storybook/preset-create-react-app',
    {
      name: '@storybook/addon-docs/preset',
      options: {
        configureJSX: true,
      },
    },
  ],
  addons: [
    '@storybook/addon-a11y/register',
    '@storybook/addon-actions/register',
    '@storybook/addon-knobs/register',
 ],
};
Vanuan commented 4 years ago

Found the source: https://github.com/storybookjs/storybook/blob/21b8a7fb2ca2ace85be14accabc5e046f9189377/addons/docs/src/frameworks/common/preset.ts#L36-L45

So, if your story filename is different, no code would be shown.

benmvp commented 4 years ago

I just ran into this same problem as well when using TypeScript. Turns out the problem was the TypeScript types. My guess is that whatever is loading/parsing the code isn't able to parse TypeScript (probably cuz it doesn't know it's TypeScript), and as a result ti fails. And then when it fails the "no code available" shows up.


EDIT: So I did more digging. After getting it to work by removing the Typescript types, I changed my parser to typescript like so in my .storybook/presets.js file:

module.exports = [
  {
    name: '@storybook/addon-docs/react/preset',
    options: {
      sourceLoaderOptions: {
        parser: 'typescript',
      },
    },
  },
];

This broke it again, I was no longer able to view the source code. After a lot of code spelunking I got to this line of code by @shilman: https://github.com/storybookjs/storybook/blame/8e8a4683afcefacab83b4169f8235254c0a2c2b0/addons/docs/src/blocks/Source.tsx#L39. When the parser is typescript the locationsMap is empty. When not, the locationsMap has the correct story ID mappings.

I don't quite understand the comment or what locationsMap is bad, so not quite sure where to go from here, but I'll do more digging if I can. The code is properly parsed by TypeScript, so there's some other part of the stack that's failing.

benmvp commented 4 years ago

Ok, I traced it down to a bug in @storybook/source-loader. The parsed AST from JS slightly differs from that from TS and the code was only handling the JS version. Luckily @shilman has already fixed the bug! It's in a RC of @storybook/source-loader I believe, but 5.3.0 hasn't officially been released, which is why @storybook/addon-docs hasn't updated to it (or maybe they all have the same versions).

Once @storybook/source-loader v5.3.0 is released and the docs addon is updated, this should be fixed. 👏🏾 The bug was fixed back in October, so not quite sure what the timeline would be on it

benmvp commented 4 years ago

Just upgraded to 5.3.3 and verified the code shows for me

shilman commented 4 years ago

🎉 closing this! 🎉

gaurav5430 commented 4 years ago

In my project I am overriding the default storybook webpack config with my own custom webpack config. This meant that any of the default module.rules from storybook webpack config were not being applied. Due to this the source-loader rule was also not being applied, and DocsPage was showing 'No code available' .

I re-added the source-loader rule and it works fine.

andre-brdoch commented 4 years ago

ran into the same issue like @Vanuan on v6.0.21. It works if the storyname includes the name of the component. However, our folder structure is like this:

/MyComponent
--> index.vue
--> stories.js

I am no webpack expert, could someone point me into a direction how to fix this without changing folder structure? 😃

EDIT: It worked by manually adding source-loader:

config.module.rules.push({
  test: /(.*\.)?stories\.js$/,
  loaders: [ '@storybook/source-loader' ],
});
Vanuan commented 4 years ago

@andre-brdoch Maybe try changing loaders[x][y].test to a constant, e.g. "stories.js".

Or, if you're willing to contribute, change this line: https://github.com/storybookjs/storybook/blob/57798a108e183d97f94fd4cc396e45e1d4b04d23/addons/docs/src/frameworks/common/preset.ts#L65

To the following:

          test: /\.?(stories|story)\.[tj]sx?$/,
pwfisher commented 3 years ago

@Vanuan

@andre-brdoch Maybe try changing loaders[x][y].test to a constant, e.g. "stories.js".

Or, if you're willing to contribute, change this line:

https://github.com/storybookjs/storybook/blob/57798a108e183d97f94fd4cc396e45e1d4b04d23/addons/docs/src/frameworks/common/preset.ts#L65

To the following:

          test: /\.?(stories|story)\.[tj]sx?$/,

but that would match, for example, History.tsx.

Assuming this tests a path and not a filename alone:

          test: /(/|\.)(stories|story)\.[tj]sx?$/,
pwfisher commented 3 years ago

Add the following to /.storybook/webpack.config.js:

// /.storybook/webpack.config.js
module.exports = ({ config }) => {
  config.module.rules.push({
    test: /(\/|\.)(stories|story)\.[tj]sx?$/,
    use: '@storybook/source-loader',
  })
  return config
}
Vanuan commented 3 years ago

@pwfisher Good catch!

shilman commented 3 years ago

@pwfisher @Vanuan what if you just add \/story.[tj]sx? i'm afraid that the source-loader would get run twice on e.g. Foo.stories.tsx because addon-docs already adds a rule for that.

penx commented 3 years ago

I'm finding that if I have a story as follows:

export const Start: Story = () => (
  <Button start>
    Save and continue
  </Button>
);

Then I get "No code available". But if I add an unused args parameter to the function:

export const Start: Story = (args) => (
  <Button start>
    Save and continue
  </Button>
);

Then "Show code" is displayed.

Not sure if this is a bug or a feature @shilman ? 😀

shilman commented 3 years ago

@penx it's a feature. you can read more about it here:

https://storybook.js.org/docs/react/writing-docs/doc-blocks#docspage-1

dynamic snippet rendering was introduced at the last minute in storybook 6.0 to fix a broken user experience for args stories, which were introduced in 6.0. however, because the feature was immature, i didn't want to break the experience for existing 5.x users writing no-args stories. now that dynamic snippets are fairly stable, we can probably make them the default and users can opt-in to source snippets when they need them. i'll consider this seriously as part of the 7.0 release.

yannbf commented 2 years ago

@shilman I found another scenario where it does not show code. When using a custom DocsContainer:

import { DocsContainer } from '@storybook/addon-docs';

export const parameters = {
  docs: {
    container: (props) => <DocsContainer {...props} />,
  },
};

Seems like a bug?

isaacebhodaghe commented 1 year ago

@shilman I found another scenario where it does not show code. When using a custom DocsContainer:

import { DocsContainer } from '@storybook/addon-docs';

export const parameters = {
  docs: {
    container: (props) => <DocsContainer {...props} />,
  },
};

Seems like a bug?

Is there a solution to this, I'm facing this same senerio