storybookjs / storybook

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

[Bug]: docs container - Storybook preview hooks can only be called inside decorators and story functions #28758

Open unional opened 1 month ago

unional commented 1 month ago

Describe the bug

I'm using storybook-dark-mode to toggle my stories between light and dark mode.

It works in stories, but the theme in the autodocs are not updated automatically.

To address that, I use a custom doc container:

// .storybook/preview.tsx
import type { Preview } from '@storybook/react';
import { DocsContainer, type DocsContextProps } from '@storybook/blocks';
import { themes } from '@storybook/theming';
import React, { type PropsWithChildren } from 'react';
import { useDarkMode } from 'storybook-dark-mode';

export interface DocsContainerProps {
  context: DocsContextProps<any>;
}
function ThemedContainer(props: PropsWithChildren<DocsContextProps>) {
  const dark = useDarkMode();
  return (
    <DocsContainer context={props} theme={dark ? themes.dark : themes.light}>
      {props.children}
    </DocsContainer>
  );
}
const preview: Preview = {
  parameters: {
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
    docs: {
      container: ThemedContainer,
    },
  },
};

export default preview;

This is working is storybook 8.1, but broken in 8.2.

Reproduction link

https://stackblitz.com/edit/github-2sfagn?file=.storybook%2Fpreview.tsx

Reproduction steps

open the repro

System

Storybook Environment Info:

  System:
    OS: macOS 14.5
    CPU: (10) arm64 Apple M1 Pro
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.12.2 - ~/.nvm/versions/node/v20.12.2/bin/node
    npm: 10.5.0 - ~/.nvm/versions/node/v20.12.2/bin/npm
    pnpm: 9.0.4 - ~/.nvm/versions/node/v20.12.2/bin/pnpm <----- active
  Browsers:
    Chrome: 127.0.6533.72
    Safari: 17.5

Additional context

No response

greptile-apps[bot] commented 1 month ago

Disclaimer This information might be inaccurate, due to it being generated automatically To fix the issue with the theme not updating in autodocs in Storybook 8.2, modify the ExternalDocsContainer component to use the useDarkMode hook and apply the theme dynamically. Here's the updated code:

import React from 'react';
import { ThemeProvider, themes, ensure } from 'storybook/internal/theming';
import { DocsContext } from '../DocsContext';
import { ExternalPreview } from './ExternalPreview';
import { useDarkMode } from 'storybook-dark-mode';

let preview: ExternalPreview<Renderer>;

export const ExternalDocsContainer: React.FC<
  React.PropsWithChildren<{ projectAnnotations: any }>
> = ({ projectAnnotations, children }) => {
  if (!preview) preview = new ExternalPreview(projectAnnotations);

  const dark = useDarkMode();
  const theme = dark ? themes.dark : themes.light;

  return (
    <DocsContext.Provider value={preview.docsContext()}>
      <ThemeProvider theme={ensure(theme)}>{children}</ThemeProvider>
    </DocsContext.Provider>
  );
};

This change ensures that the theme in the autodocs updates automatically based on the dark mode setting.

References

/.github/DISCUSSION_TEMPLATE/help.yml /docs/_snippets/storybook-preview-auto-docs-override-mdx-container.md /.github/DISCUSSION_TEMPLATE/ideas.yml /docs/_snippets/storybook-preview-docs-dark-theme.md /docs/_snippets/configure-mock-provider-with-story-parameter.md /code/lib/blocks/src/blocks/external/ExternalDocsContainer.tsx /code/lib/blocks/src/blocks/external /docs/_snippets/storybook-preview-with-angular-polyfills.md /test-storybooks/external-docs/.storybook /.github/DISCUSSION_TEMPLATE/rfc.yml /code/addons/docs/template/stories/docspage /code/core/src/manager/components/sidebar/Brand.tsx /code/core/scripts/helpers/modifyThemeTypes.ts /code/lib/cli/src/automigrate/fixes/autodocs-tags.test.ts /code/addons/docs/angular /code/addons/docs/ember /code/frameworks/ember/src/client/preview/docs /code/addons/docs/template/stories/docspage/override.stories.ts /code/.storybook/preview.tsx /docs/_snippets/mock-provider-in-preview.md /code/core/src/core-server/utils/mockdata/errors/MetaOfClashingDefaultName.mdx /code/addons/docs/react /docs/_snippets/storybook-preview-empty-sort-object.md /code/core/src/preview-api/modules/preview-web/docs-context /docs/_snippets/storybook-preview-optout-inline.md

#### About Greptile This response provides a starting point for your research, not a precise solution. Help us improve! Please leave a 👍 if this is helpful and 👎 if it is irrelevant. [Ask Greptile](https://app.greptile.com/chat/github/storybookjs/storybook/next) · [Edit Issue Bot Settings](https://app.greptile.com/apps/github)
unional commented 1 month ago

FYI I also tried to make it work with @storybook/addon-themes and here is the ThemedContainer that works:

import { DocsContainer, type DocsContextProps } from '@storybook/blocks'
import { themes } from '@storybook/theming'
import { type PropsWithChildren } from 'react'

export function ThemedContainer(props: PropsWithChildren<{ context: DocsContextProps }>) {
    return (
        <DocsContainer
            context={props.context}
            theme={(props.context as any).store.globals.globals.theme === 'dark' ? themes.dark : themes.light}
        >
            {props.children}
        </DocsContainer>
    )
}

You can see I dive in to the internals to get the theme value which is probably not a good idea. And alternative is parsing the globals query params.

mellm0 commented 1 month ago

We have the same issue for themes, and consequentially found the same solution as @unional, but yes it seems very hacky.

Allowing addon hooks to be used in DocsContainer would be really handy.

massi08 commented 1 month ago

Same issue here, and workaround doesn't seem to work for us on version 8.2.7

mohitkyadav commented 2 weeks ago

Same issue for me as well, here's the code that breaks:

import React from 'react'
import { DocsContainer as SBDocsContainer } from '@storybook/blocks'
import { useDarkMode } from 'storybook-dark-mode'

import { darkTheme, lightTheme } from '../themes'

const DocsContainer = ({ children, context }) => (
  <SBDocsContainer
    context={context}
    theme={useDarkMode() ? darkTheme : lightTheme}
  >
    {children}
  </SBDocsContainer>
)

export default DocsContainer

For me store.globals.globals.theme is undefined.