Storybook: Source: Dynamic snippets includes decorators

Created on 14 Aug 2020  路  22Comments  路  Source: storybookjs/storybook

Describe the bug
When using decorator, viewing of the story would show the wrapper markup of the decorator and <No Display name /> for the story.

Expected behavior
It should only render the Story and not show the markup of the decorator

Screenshots
Screenshot 2020-08-14 at 13 09 08

Code snippets

export default {
  title: "Atom/Input/Helper",
  component: Helper,


  decorators: [
    (Story: any) => (
      <div style={{ position: "relative" }}>
        <Story />
      </div>
    ),
  ],
};

const Template: Story<HelperProps> = (args) => (
  <Helper {...args} />
);

export const Default = Template.bind({});
Default.args = {
  message: "Please do this as soon as you do that.",
};

System:

Environment Info:
  Binaries:
    Node: 14.2.0 - ~/.nvm/versions/node/v14.2.0/bin/node
    Yarn: 1.22.4 - /usr/local/bin/yarn
    npm: 6.14.7 - ~/.nvm/versions/node/v14.2.0/bin/npm
  Browsers:
    Chrome: 84.0.4147.125
    Firefox: 77.0.1
    Safari: 13.1.2
  npmPackages:
    @storybook/addon-docs: ^6.0.6 => 6.0.6 
    @storybook/addon-essentials: ^6.0.6 => 6.0.6 
    @storybook/addon-knobs: ^6.0.6 => 6.0.6 
    @storybook/addon-links: ^6.0.6 => 6.0.6 
    @storybook/addon-notes: ^5.3.19 => 5.3.19 
    @storybook/addon-storysource: ^6.0.6 => 6.0.6 
    @storybook/addon-viewport: ^6.0.6 => 6.0.6 
    @storybook/addons: ^6.0.6 => 6.0.6 
    @storybook/preset-create-react-app: ^3.1.4 => 3.1.4 
    @storybook/react: ^6.0.6 => 6.0.6 
P0 docs source bug has workaround

Most helpful comment

To add some info, if I use my decorator in the following format, it works:

decorators: [
  story => <div style={{height: "50vh"}}>{story()}</div>
]

Hope this might help in debugging 馃檪

All 22 comments

Unrelated, but this is probably not a good idea:

    @storybook/addon-notes: ^5.3.19 => 5.3.19 

I'm having the exact same issue.

To add some info, if I use my decorator in the following format, it works:

decorators: [
  story => <div style={{height: "50vh"}}>{story()}</div>
]

Hope this might help in debugging 馃檪

Actually that seems to work... but it still wraps the story in the provided decorator, isn't it meant to only show the story

Actually that seems to work... but it still wraps the story in the provided decorator, isn't it meant to only show the story

Same here.

I see this:

<[object Object]
  value={{
    authenticated: {
      id: 'test'
    },
    setUser: () => {},
    signUp: function noRefCheck() {},
    user: {
      id: 'test'
    }
  }}
>
  <ApolloProvider client={[object Object]}>
    <MyComponent
          ...
    />
  </ApolloProvider>
</[object Object]>

When all I want to see is <MyComponent />

As a workaround you can set the docs.source.type story parameter to "code".

@shilman doesn't work

@Quadriphobs1 do you have a repro i can look at?

As a workaround you can set the docs.source.type story parameter to "code".

Where can this be set?

Something like:

export default {
  title: "Atom/Input/Helper",
  component: Helper,
  parameters: { docs: { source: { type: 'code' } } }
}

@shilman Using that produces no code available actually when the decorator is still provided

export default {
  title: "Atom/Component",
  component: Component,
  decorators: [
    (story: any) => {
      return <div>{story()}</div>;
    },
  ],
  parameters: { docs: { source: { type: 'code' } } }
};

When setting parameters as above "Show Code" window returns (args) => <Component {...args} /> for me. Same whether using a decorator or not.

I'm still facing this issue, any workaround available

Me too. What is the reason for that problem? Maybe a tsconfig or webpack problem?
The examples run perfectly fine though.

It's a bit hacky, but I got around this by adding a decorator to wrap the story with known strings. Then I used transformSource to extract just the bit inside...

export default {
  title: 'Atom/Input/Helper',
  component: Helper,

  decorators: [
    story => (
      <>
        <span data-begin-source />
        {story()}
        <span data-end-source />
      </>
    ),
    (story: any) => <div style={{ position: 'relative' }}>{story()}</div>,
  ],

  parameters: {
    docs: {
      transformSource: source => {
        const [, output] =
          source.match(
            /<span data-begin-source \/>([\s\S]*?)<span data-end-source \/>/,
          ) ?? []
        return output
      },
    },
  },
}

Screenshot 2020-11-10 at 17 28 03

Hi 馃憢 ,

I fixed this by replacing:

<Story />

by:

{Story()}

I got this to work but only globally by adding a decorator to the .storybook/preview.js file. Mine looks like this. I needed to include global styles available for all components but was running into the same issue. I get the same "No Display Name" issue when trying to add to decorators at the individual story level. Using Storybook version 6.1.4.

import React from 'react';

import { GlobalStyle } from '../src/styles/styles';

// Global decorator to apply the styles to all stories
export const decorators = [
  (Story) => (
    <>
      <GlobalStyle />
      <Story />
    </>
  ),
];

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
};

It appears that render props in a component- or story-level decorator kill dynamic docs. None of the above workarounds are working for me.

One can replicate this when working on primitives for forms while using Formik.

import React from 'react';
import { Story, Meta } from '@storybook/react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import { noop } from 'lodash';
import { StoryFnReactReturnType } from '@storybook/react/dist/client/preview/types';
import { Button } from '../../src/Button';
import { Input, InputProps } from '../../src/Forms/Input';

const fieldName = 'test';

const FormikDecorator = (Story: () => StoryFnReactReturnType) => {
  const validationSchema = Yup.object({ [fieldName]: Yup.string().required().default('') });
  const initialValues = validationSchema.cast({})!;

  return (
    <Formik validationSchema={validationSchema} initialValues={initialValues} onSubmit={noop}>
      {() => (
        <Form>
          <Story />

          <Button type="submit" variant="button-filled-blue">
            Submit
          </Button>
        </Box>
      )}
    </Formik>
  );
};

const meta: Meta<InputProps> = {
  title: 'Zephyr/Forms/Input',
  component: Input,
  decorators: [FormikDecorator],
};

export default meta;

const Template: Story<InputProps> = (args) => (
  <Input {...args} label="Some Input" name={fieldName} data-testid={meta.title} />
);

export const Default = Template.bind({}) as typeof Template;

Default.args = {
  type: 'text',
  isLabelHidden: false,
  required: false,
  autoComplete: 'off',
};

Default.parameters = {
  docs: {
    description: {
      story: 'An input!',
    },
  },
};

The resultant visible source code:

<Formik
  initialValues={{
    test: ''
  }}
  onSubmit={() => {}}
  validationSchema={[object Object]}
/>

If I try doing docs.source.type = 'code' in my Story's parameters, I end up with "No code available".

@kylemh are you using addon-storysource? If so, can you try removing it?

I'm not using that addon.

module.exports = {
  stories: ['../packages/**/*.stories.{ts,tsx,js,jsx,mdx}'],
  addons: ['@storybook/addon-links', '@storybook/addon-actions', '@storybook/addon-essentials'],
};

It's this PR if you're interested in diving deeper: https://github.com/AirLabsTeam/air-core/pull/58

Hey @kylemh there's a bug in source-loader when it comes to casting the result of Template.bind({}). Here's the workaround:

export const Default = Template.bind({}); // as typeof Template;

Still the same problem, but I didn't mean to have that, so thanks for the spot!

Was this page helpful?
0 / 5 - 0 ratings