storybookjs / storybook

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

[Bug]: Storybook 7.x storysource addon doesn't show whole story code #22922

Open Podlipny opened 1 year ago

Podlipny commented 1 year ago

Describe the bug

I have a problem with the Storybook addon Storysource after migration from Storybook 6.x to Storybook 7.x. In Storybook 6.x I was able to see the whole story code, but now I am able only to see args content.

image

To Reproduce

here is repo: https://github.com/Podlipny/storybook-storysource-issue

just npm i and npm storybook, tab code should contain whole story code

System

Storybook 7 and newer

Additional context

No response

KevinGhadyani-Okta commented 1 year ago

This looks like an issue with the AST. I'm making some wild guesses here, but it's probably doing a check inside the Component, and now the component is an object.

I wonder if simply changing it to look at storyObject.render instead of the storyObject would fix it.

Podlipny commented 1 year ago

repo with code to replicate this issue is here: https://github.com/Podlipny/storybook-storysource-issue

I am defining story based on storybook documentation, so this should wor without any need to modify anything

import type { Meta, StoryObj } from '@storybook/react';

import { Button } from './Button';

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta = {
  title: 'Example/Button',
  component: Button,
  tags: ['autodocs'],
  argTypes: {
    backgroundColor: { control: 'color' },
  },
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Primary: Story = {
  render: () => <Button primary label="Button" />,
  args: {
    primary: true,
    label: 'Button sdsd sdsd sdsds',
  },
};
yingpengsha commented 1 year ago

Is there any progress and possible workaround on this issue?

Podlipny commented 1 year ago

nothing I would know about - we just stopped using storysource until this bug is resolved

gluharry commented 1 year ago

I am also able to reproduce this after migrating from 6.x to 7.5. Is there any progress or problem identification on this?

Cheers!

valentinpalkovic commented 12 months ago

Can someone help me to understand the issue?

As far as I understand it, in Storybook 6.x the whole Storyfile was outputted as source code. Now, in 7.x, only the code is outputted, which applies to the selected Story. Isn't this the wished behavior? Why should someone be interested in showing the source code from the other Stories when one is selected? Wouldn't this be a mess for huge Story files? I am missing some context here.

aaron-troglia commented 12 months ago

I am also seeing this issue on v7.5. The information outlined in "Displaying full source" on the landing page does not appear to make a difference.

@valentinpalkovic, The end goal isn't to see other stories' source code, but to see the full component code of the corresponding story that is selected. Right now, we're just seeing the args.

Screenshot of the current result: Screenshot 2023-11-15 at 3 48 01 PM

Compared to the desired (screenshot pulled from the Storysource landing page gif) Screenshot 2023-11-15 at 3 49 11 PM

sumitdahal7 commented 10 months ago

I also encountered the same problem while displaying a source code by using storysource. Has anyone got the solutions of it ??

JamesIves commented 10 months ago

Seeing the same issue:

Output shows the following:

{
  args: {
    ['icon-group']: 'general'
  }
}

versus the desired:

<component-name icon-group="general"></component-name>

The code preview in the doc view works just fine, which is ultimately what I want in the addon view that updates with the controls.

mm-georges commented 9 months ago

Could anyone find a workaround to this issue? :(

SalahAdDin commented 9 months ago

Here I am, same issue: image

aaron-troglia commented 9 months ago

I found that there is a storySource parameter you can add to override what the rendered story code can be.

For example:

Button.stories.ts

const meta = {
  title: 'Example/Button',
  component: Button,
  parameters: {
    layout: 'centered',
    storySource: {
      source: 'foo' //Put your code here
    }
  },

Based on how the addon works, it looks like it tries to load the loaderSource first, then defaults to an object of the args:

/core/addons/storysource/src/StoryPanel.tsx

export const StoryPanel: React.FC<StoryPanelProps> = ({ api }) => {
  const story = api.getCurrentStoryData();
  const selectedStoryRef = React.useRef<HTMLDivElement>(null);
  //The param I'm passing in the above example
  const { source: loaderSource, locationsMap }: SourceParams = useParameter('storySource', {});
  const { source: { originalSource: docsSource } = {} }: DocsParams = useParameter('docs', {});
  // prefer to use the source from source-loader, but fallback to
  // source provided by csf-plugin for vite usage
  const source = loaderSource || docsSource || 'loading source...';
Podlipny commented 9 months ago

thanks for response guys, We've been able to make it work with this inside `.storybook/preview

export const parameters = {
  docs: { source: { type: 'code' } },
...
SalahAdDin commented 9 months ago

docs: { source: { type: 'code' } },

It does not work for us.

ChenReuven commented 9 months ago

is there any update? it seems that there is a lot of issues with the show code , source code mechanism

SalahAdDin commented 9 months ago

Ok, the issue happens when you have an empty storybook, so the automatic placeholder for the addon is showing the default arguments for the storybook, and it should be clear on the plugin documentation.

Of course, it is not clear cause by logic you should write your story before checking it in a storybook.

ddamato commented 8 months ago

Also having this problem.

It's a monorepo using @storybook/addon-essentials, I have a root configuration which is referenced in the leaf packages for individual component projects. When I was migrating to SB7, some of the stories would have this problem. Others would render as expected. It wasn't immediately clear to me what the difference between the projects was; they are have the same or share configurations between them with varying degrees of upkeep.

Checking today, now they are all having this problem. I don't know what has caused this update, since there's different kinds of changes happening in the repo all the time.

Referring to the documentation as @SalahAdDin suggested, there is a mention in the @storybook/addon-storysource docs with the following message:

Storybook 6.0 introduced an unintentional change to source-loader, in which only the source of the selected story is shown in the addon. To restore the old behavior, pass theinjectStoryParameters: false option.

The examples there are confusing to me. It suggests that @storybook/addon-docs has an option called sourceLoaderOptions. Visiting the docs for that plugin (which is inside of @storybook/addon-essentials) has no mention of that option; only csfPluginOptions and mdxPluginOptions.

So if one was to update the behavior through options within @storybook/addon-essentials, what is the correct configuration?

oeem1011 commented 8 months ago

Looking also for a valid solution.

aaron-troglia How did you put your code into the storySourceobject? I tried it with raw-loader, but failed, since it converts my .tsfiles into JS

anibe commented 8 months ago

This issue appears to be fixed in 8.x but it would be nice if a fix is made available for those who can't upgrade yet 🙏

brian-patrick-3 commented 8 months ago

Since upgrading to Storybook v8 I can only see the current story source, and there is no way to reveal the full story source.

aaron-troglia commented 8 months ago

@oeem1011 I also ran in into a similar issue when trying out a few tactics. My initial thought was to simply convert the exported component into a string:

import { Button } from './Button';

const meta = {
  title: 'Example/Button',
  component: Button,
  parameters: {
    layout: 'centered',
    storySource: {
      source: `${Button}`
    }
  }, //...continued

But like you said it coverts the code. It might work for some crude use-cases, but isn't a great option.

mm-georges commented 8 months ago

after upgrading to storybook 8, still running into the same issue :(

ddamato commented 8 months ago

after upgrading to storybook 8, still running into the same issue :(

Sorry to hear that but also glad I didn't go through the upgrade just for this now. Clearly, there's something else going on.

mm-georges commented 7 months ago

Any chance this will be fixed in the future?

sdandrader commented 7 months ago

Same issue here! @valentinpalkovic there is any chance, please?

prokhvit commented 6 months ago

Still actual

brynshanahan commented 5 months ago

We're also getting this in 7.0 and 8.0.

When running the dev server with Vite the story source appears correctly, but then when building for production we get {} as the story source.

The issue only appears when displaying stories in a custom .mdx file. When using autodocs the source appears correctly which makes me think it's not to do with source parsing?

wondasom commented 5 months ago

It is unfortunate that it is still happening. Our team used to use .mdx as default doc format but recently decided on the following as an alternative and now working on changing existing docs 🥲


const meta: Meta<typeof COMPONENT> = {
  title: 'CATEGORY/COMPONENT',
  parameters: {
    docs: {
      description: {
        component:
          '🖊️ COMPONENT DESCRIPTION GOES HERE', // 👈 You can pass a readme file here as well. 
      },
    },
    ...
  },
}

export default meta
type Story = StoryObj<typeof COMPONENT>

...

export const Default: Story = (args: COMPONENT_PROPS) => {
  return (
    ...
  )
}

Default.parameters = {
  docs: {
    description: {
      story: '🖊️ STORY DESCRIPTION GOES HERE',
    },
  },
}
Zlvsky commented 5 months ago

Any updates/workarounds?:(

brynshanahan commented 5 months ago

@Zlvsky if you check out @sashathor's branch it appears they've used a custom component that parses the story source instead of relying on the component

Zlvsky commented 5 months ago

@brynshanahan Alright, thanks!

Also I've made workaround that works for me to view the file source code:

  1. Create separate file with exported source code as a string (if you use webpack, you can try raw-loader)
export const code = `
// your code here
`
  1. Import it on Component story and pass with parameters
import { code } from './Component.code'

export const Primary: Story = (args) => {
  return (
    <Component {...args}>
      Primary
    </Component>
  )
}

Primary.parameters = {
  storySource: {
    source: code
  }
}

image

Omar-Assadi-copilot commented 3 months ago

Any updates?

ilchenkoArtem commented 2 months ago

Any updates?

brynshanahan commented 2 months ago

We managed to fix this issue. We tracked it down to React contexts being imported twice from the same package, in one build we were getting module.cjs and module.mjs of the same module. The culprit was our custom Rollup resolver plugin. We were able to fix it by changing our custom resolver plugin to use this.resolve so it would use Vite's default resolving rather than just straight require.resolve

ddamato commented 2 months ago

It's still happening on my project, but less so than I reported earlier. Out of 70 components, this seems to affect 7 of them. Also noticed this because where it happens, the Storybook Controls are inert in the docs. More specifically, the first story in the file used as the docs example renders knobs that do nothing. And the code shows precisely what is written in source instead of what is meant to be written as a component.

// Expected in Story Source
<Tabs id='default-tabs' />
// Actual in Story Source
{
  args: { id: 'default-tabs' }
  render: function Default(args) { return <Tabs { ...args }/> }
}

Note that I have plenty of other stories that utilize render that do not have this problem. I'm well aware the render function I'm giving in this example seems excessive. I've omitted the parts within for the sake of simplicity in the example. I also have a component that simply displays {} as is in the source.

Also note that if I fire up the local Storybook for examples that do not work, the Storybook Controls do work on the individual story pages. In my setup, this seems to be a problem specifically when rendering docs for certain components that don't seem to be different from others that are working.

shilman commented 2 months ago

@ddamato is the behavior identical in dev and build modes?

ddamato commented 2 months ago

@ddamato is the behavior identical in dev and build modes?

Yes identical behavior between both.

shilman commented 2 months ago

@ddamato is this the literal story source?

{
  args: { id: 'default-tabs' }
  render: function Default(args) { return <Tabs { ...args }/> }
}

If so, can you try changing it to the following and see if that fixes anything:

{
  args: { id: 'default-tabs' }
  render: (args) => { return <Tabs { ...args }/> }
}
ddamato commented 2 months ago

Yes, that is the literal source. That change doesn't seem to affect the output past showing the new source.

mm-georges commented 1 month ago

Is this getting fixed? We really miss the old behaviour of the Storysource in our Project.

yasevplaton commented 1 month ago

Hey, guys! We've also encountered this issue in 8.3.3, and did a little analysis, here are the results (to clarify, there are screenshots with storysource-addon code below).

  1. In 7.5.2 we have storySource.source with full file content in story's parameters, this is convenient because in this case we see all scope for the current story. We would like to have this behaviour, at least it should be managed by parameter. Expected result Image

  2. In 8.1.10 we don't have storySource and docs.source.originalSource properties at all and just see "loading source..." in code panel. Image

  3. In 8.3.3 we also don't have storySource property, and we could work only with docs.source.originalSource that just refers to object with arguments for story. This is not convenient especially for basic story (in this case we just have empty object as code for the story). Actual result Image

Storybook config

const path = require('path');  
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');  
import { StorybookConfig } from '@storybook/react-webpack5'  

const config: StorybookConfig = {  
  stories: [  
    '../docs/**/*.@(mdx|stories.@(ts|tsx))',  
    '../docs/*.@(mdx|stories.@(ts|tsx))',  
    '../src/**/*.@(mdx|stories.@(ts|tsx))'  
  ],  
  addons: [  
    '@storybook/addon-docs',  
    '@storybook/addon-essentials',  
    '@storybook/addon-storysource',  
    '@storybook/addon-links',  
    'storybook-dark-mode',  
    '@storybook/addon-webpack5-compiler-babel'  
  ],  
  managerHead: () => `  
    <style>      .css-1wmxdc3, .css-1nwijr1 {        margin-right: 0 !important;      }      .css-1rb1jn6 {        max-width: 100% !important;      }    </style>  `,  
  framework: '@storybook/react-webpack5',  
  webpackFinal: async (config) => {  
    if (!config.resolve || !config.module || !config.module.rules) {  
      return config  
    }  
    config.resolve.alias = {  
      ...config.resolve.alias,  
      '@src': path.resolve(__dirname, '../src/'),  
      '@design-system': path.resolve(__dirname, '../design-system/'),  
      '@icons': path.resolve(__dirname, '../icons/'),  
      '@helpers': path.resolve(__dirname, '../helpers/'),  
      '@style': path.resolve(__dirname, '../style/'),  
      '@sb': path.resolve(__dirname, '../.storybook/'),  
    };  
    config.module.rules.push({  
      test: /\.less$/,  
      include: [  
        path.resolve(__dirname, '..'),  
        path.resolve(__dirname, '..', 'node_modules', 'antd')  
      ],  
      use: [  
        require.resolve('style-loader'),  
        {  
          loader: require.resolve('css-loader'),  
          options: {  
            modules: {  
              mode: 'global',  
              localIdentName: '[name]_[local]_[hash:base64:8]'  
            },  
            sourceMap: true  
          }  
        },  
        {  
          loader: require.resolve('less-loader'),  
          options: {  
            lessOptions: {  
              paths: [path.resolve(__dirname, '..')],  
              javascriptEnabled: true  
            }  
          }  
        }  
      ]  
    })  

    config.resolve.plugins = [  
      ...(config.resolve.plugins || []),  
      new TsconfigPathsPlugin({  
        extensions: config.resolve.extensions  
      })  
    ]  

    return config  
  }  
}  

module.exports = config

preview.ts

import { Preview } from '@storybook/react'  
import { badgesConfig } from './badges'  
import { withI18n } from './decorators/withI18n'  
import { themes } from '@storybook/theming'  
import { CustomDocsContainer } from './Docs'  
import { withThemeProvider } from './decorators/withThemeProvider'  
import { ThemeKey } from '@design-system/types'  
import '../style/styles.less'  
import { themeColors } from '@design-system/tokens'  
import { withBadges } from '@sb/decorators/withBadges'  

import lightThemeLogo from './assets/Hexa_UI_Light.svg'  
import darkThemeLogo from './assets/Hexa_UI_Dark.svg'  

export const globalTypes = {  
  theme: {  
    name: 'Theme',  
    descriptions: 'Global theme for components',  
    defaultValue: ThemeKey.Light  
  },  
  version: {  
    name: 'Version',  
    descriptions: 'UI Kit Versions',  
    defaultValue: 'hexa-ui'  
  },  
  locale: {  
    name: 'Locale',  
    descriptions: 'Localization language',  
    toolbar: {  
      icon: 'globe',  
      items: [  
        { value: 'en-us', title: 'English' },  
        { value: 'ru-ru', title: 'Русский' },  
        { value: 'de-de', title: 'Deutsch' },  
        { value: 'ja-jp', title: '日本語' },  
        { value: 'fr-fr', title: 'Français (France)' },  
        { value: 'it-it', title: 'Italiano' },  
        { value: 'es-es', title: 'Español (España)' },  
        { value: 'es-mx', title: 'Español (Mexico)' },  
        { value: 'pt-pr', title: 'Português (Brasil)' },  
        { value: 'tr-tr', title: 'Türkçe' },  
        { value: 'pl-pl', title: 'Polski' },  
        { value: 'ko-kr', title: '한국어' },  
        { value: 'ar-ae', title: 'العربية' },  
        { value: 'kk-kz', title: 'Қазақша' },  
        { value: 'zh-hans', title: '中文(简体)' },  
        { value: 'zh-hant', title: '中文(台灣)' },  
        { value: 'hash-id', title: 'Hash ID' },  
      ],  
      showName: true,  
    },  
  },  
}  

const preview: Preview = {  
  decorators: [  
    withI18n,  
    withThemeProvider,  
    withBadges  
  ],  
  argTypes: {  
    componentId: { control: false },  
    dataTestId: { control: false },  
  },  
  parameters: {  
    docs: {  
      container: CustomDocsContainer,  
    },  
    darkMode: {  
      light: { ...themes.light, appBg: themeColors.bg.base.light, brandImage: lightThemeLogo.toString() },  
      dark: { ...themes.dark, appBg: themeColors.bg.base.dark, brandImage: darkThemeLogo.toString() },  
    },  
    controls: {  
      expanded: true,  
      matchers: {  
        color: /(background|color)$/i,  
        date: /Date$/  
      }  
    },  
    badgesConfig  
  },  
  tags: ['autodocs']  
}  
export default preview

It seems that some changes in source-loader between 7.5.2 and 8.1.10 (8.3.3) have led to the current behavior. So here are two questions

  1. Is this a bug or feature? Looks like it should be managed by some parameter (for example showFullSource) for source-loader. If this is a bug, I could and I would like to contribute and help.
  2. Do you have 5-30 minutes for a small call to navigate through the codebase?

@shilman @valentinpalkovic

SalahAdDin commented 1 month ago

Hey, guys! We've also encountered this issue in 8.3.3, and did a little analysis, here are the results (to clarify, there are screenshots with storysource-addon code below).

1. In 7.5.2 we have `storySource.source` with full file content in story's parameters, this is convenient because in this case we see all scope for the current story. We would like to have this behaviour, at least it should be managed by parameter. **Expected result** ![Image](https://github.com/user-attachments/assets/57997ef6-7ec4-4f29-8e83-2b57ba224e6d)

2. In 8.1.10 we don't have `storySource` and `docs.source.originalSource` properties at all and just see `"loading source..."` in code panel. ![Image](https://github.com/user-attachments/assets/11af16c8-b66c-45d1-a5e0-ce1e7b31ced8)

3. In 8.3.3 we also don't have `storySource` property, and we could work only with `docs.source.originalSource` that just refers to object with arguments for story. This is not convenient especially for basic story (in this case we just have empty object as _code_ for the story). **Actual result** ![Image](https://github.com/user-attachments/assets/1fc71408-fdaa-4aff-8020-34a410a448e5)

Storybook config

const path = require('path');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
import { StorybookConfig } from '@storybook/react-webpack5'

const config: StorybookConfig = {
stories: [
'../docs//.@(mdx|stories.@(ts|tsx))',
'../docs/
.@(mdx|stories.@(ts|tsx))',
'../src/
/*.@(mdx|stories.@(ts|tsx))'
],
addons: [
'@storybook/addon-docs',
'@storybook/addon-essentials',
'@storybook/addon-storysource',
'@storybook/addon-links',
'storybook-dark-mode',
'@storybook/addon-webpack5-compiler-babel'
],
managerHead: () => `

`, framework: '@storybook/react-webpack5', webpackFinal: async (config) => { if (!config.resolve || !config.module || !config.module.rules) { return config } config.resolve.alias = { ...config.resolve.alias, '@src': path.resolve(__dirname, '../src/'), '@design-system': path.resolve(__dirname, '../design-system/'), '@icons': path.resolve(__dirname, '../icons/'), '@helpers': path.resolve(__dirname, '../helpers/'), '@style': path.resolve(__dirname, '../style/'), '@sb': path.resolve(__dirname, '../.storybook/'), }; config.module.rules.push({ test: /\.less$/, include: [ path.resolve(__dirname, '..'), path.resolve(__dirname, '..', 'node_modules', 'antd') ], use: [ require.resolve('style-loader'), { loader: require.resolve('css-loader'), options: { modules: { mode: 'global', localIdentName: '[name]_[local]_[hash:base64:8]' }, sourceMap: true } }, { loader: require.resolve('less-loader'), options: { lessOptions: { paths: [path.resolve(__dirname, '..')], javascriptEnabled: true } } } ] }) config.resolve.plugins = [ ...(config.resolve.plugins || []), new TsconfigPathsPlugin({ extensions: config.resolve.extensions }) ] return config } } module.exports = config **preview.ts** import { Preview } from '@storybook/react' import { badgesConfig } from './badges' import { withI18n } from './decorators/withI18n' import { themes } from '@storybook/theming' import { CustomDocsContainer } from './Docs' import { withThemeProvider } from './decorators/withThemeProvider' import { ThemeKey } from '@design-system/types' import '../style/styles.less' import { themeColors } from '@design-system/tokens' import { withBadges } from '@sb/decorators/withBadges' import lightThemeLogo from './assets/Hexa_UI_Light.svg' import darkThemeLogo from './assets/Hexa_UI_Dark.svg' export const globalTypes = { theme: { name: 'Theme', descriptions: 'Global theme for components', defaultValue: ThemeKey.Light }, version: { name: 'Version', descriptions: 'UI Kit Versions', defaultValue: 'hexa-ui' }, locale: { name: 'Locale', descriptions: 'Localization language', toolbar: { icon: 'globe', items: [ { value: 'en-us', title: 'English' }, { value: 'ru-ru', title: 'Русский' }, { value: 'de-de', title: 'Deutsch' }, { value: 'ja-jp', title: '日本語' }, { value: 'fr-fr', title: 'Français (France)' }, { value: 'it-it', title: 'Italiano' }, { value: 'es-es', title: 'Español (España)' }, { value: 'es-mx', title: 'Español (Mexico)' }, { value: 'pt-pr', title: 'Português (Brasil)' }, { value: 'tr-tr', title: 'Türkçe' }, { value: 'pl-pl', title: 'Polski' }, { value: 'ko-kr', title: '한국어' }, { value: 'ar-ae', title: 'العربية' }, { value: 'kk-kz', title: 'Қазақша' }, { value: 'zh-hans', title: '中文(简体)' }, { value: 'zh-hant', title: '中文(台灣)' }, { value: 'hash-id', title: 'Hash ID' }, ], showName: true, }, }, } const preview: Preview = { decorators: [ withI18n, withThemeProvider, withBadges ], argTypes: { componentId: { control: false }, dataTestId: { control: false }, }, parameters: { docs: { container: CustomDocsContainer, }, darkMode: { light: { ...themes.light, appBg: themeColors.bg.base.light, brandImage: lightThemeLogo.toString() }, dark: { ...themes.dark, appBg: themeColors.bg.base.dark, brandImage: darkThemeLogo.toString() }, }, controls: { expanded: true, matchers: { color: /(background|color)$/i, date: /Date$/ } }, badgesConfig }, tags: ['autodocs'] } export default preview It seems that some changes in source-loader between 7.5.2 and 8.1.10 (8.3.3) have led to the current behavior. So here are **two questions** 1. Is this a bug or feature? Looks like it should be managed by some parameter (for example `showFullSource`) for source-loader. If this is a bug, I could and I would like to contribute and help. 2. Do you have 5-30 minutes for a small call to navigate through the codebase? [@shilman](https://github.com/shilman) [@valentinpalkovic](https://github.com/valentinpalkovic)

I think it will require a PR.