Storybook: 5.2 -> 5.3 dynamic story creation

Created on 13 Jan 2020  路  4Comments  路  Source: storybookjs/storybook

Describe the bug
Previously I was dynamically adding stories, in CSF this I don't think is longer possible but I also noticed that knobs stopped working if I nested them.

To Reproduce
Steps to reproduce the behavior:

  1. add knobs to a component
  2. create a storyFn which uses that component
  3. see knobs break due to nesting usage

Expected behavior
Have knobs work when nested in a component.

Screenshots
If applicable, add screenshots to help explain your problem.

Code snippets
previous code:

import React from 'react';
import { storiesOf } from '@storybook/react';
import { withKnobs, select, boolean } from '@storybook/addon-knobs';
import { FontAwesomeIcon, library } from '..';
import startCase from 'lodash/startCase';

export const useKnobs = ({ prefix }) => {
  const iconsArray = Object.keys(library.definitions[prefix]);

  const border = boolean('Border', false);
  const fixedWidth = boolean('Fixed Width', false);
  const flip = select(
    'Flip',
    {
      Horizontal: 'horizontal',
      Vertical: 'vertical',
      Both: 'both',
      None: null,
    },
    null
  );
  const iconName = select(
    'Name',
    iconsArray.reduce(
      (prefixes, prefix) => ({ ...prefixes, [startCase(prefix)]: prefix }),
      {}
    ),
    iconsArray[0]
  );
  const inverse = boolean('Inverse', false);
  const listItem = boolean('List Item', false);
  const pull = select(
    'Pull',
    {
      Left: 'left',
      Right: 'right',
      None: null,
    },
    null
  );
  const pulse = boolean('Pulse', false);
  const rotation = select(
    'Rotation',
    { 90: 90, 180: 180, 270: 270, None: null },
    null
  );
  const size = select(
    'Size',
    {
      '1x': '1x',
      '2x': '2x',
      '3x': '3x',
      '4x': '4x',
      '5x': '5x',
      '6x': '6x',
      '7x': '7x',
      '8x': '8x',
      '9x': '9x',
      '10x': '10x',
      xs: 'xs',
      sm: 'sm',
      lg: 'lg',
    },
    '1x'
  );
  const spin = boolean('Spin', false);

  return {
    border,
    fixedWidth,
    flip,
    iconName,
    inverse,
    listItem,
    pull,
    pulse,
    rotation,
    size,
    spin,
  };
};

const stories = storiesOf('UI|FontAwesomeIcon', module).addDecorator(withKnobs);

Object.keys(library.definitions).forEach(prefix => {
  stories.add(prefix, () => {
    const {
      border,
      fixedWidth,
      flip,
      iconName,
      inverse,
      listItem,
      pull,
      pulse,
      rotation,
      size,
      spin,
    } = useKnobs({ prefix });

    return (
      <FontAwesomeIcon
        border={border}
        fixedWidth={fixedWidth}
        flip={flip}
        icon={[prefix, iconName]}
        inverse={inverse}
        listItem={listItem}
        pull={pull}
        pulse={pulse}
        rotation={rotation}
        size={size}
        spin={spin}
      />
    );
  });
});

Updated Code (which breaks knobs):

import React from 'react';
import PropTypes from 'prop-types';
import { withKnobs, select, boolean } from '@storybook/addon-knobs';
import { FontAwesomeIcon, library } from '..';
import startCase from 'lodash/startCase';

export const useKnobs = ({ prefix }) => {
  const iconsArray = Object.keys(library.definitions[prefix]);

  const border = boolean('Border', false);
  const fixedWidth = boolean('Fixed Width', false);
  const flip = select(
    'Flip',
    {
      Horizontal: 'horizontal',
      Vertical: 'vertical',
      Both: 'both',
      None: null,
    },
    null
  );
  const iconName = select(
    'Name',
    iconsArray.reduce(
      (prefixes, prefix) => ({ ...prefixes, [startCase(prefix)]: prefix }),
      {}
    ),
    iconsArray[0]
  );
  const inverse = boolean('Inverse', false);
  const listItem = boolean('List Item', false);
  const pull = select(
    'Pull',
    {
      Left: 'left',
      Right: 'right',
      None: null,
    },
    null
  );
  const pulse = boolean('Pulse', false);
  const rotation = select(
    'Rotation',
    { 90: 90, 180: 180, 270: 270, None: null },
    null
  );
  const size = select(
    'Size',
    {
      '1x': '1x',
      '2x': '2x',
      '3x': '3x',
      '4x': '4x',
      '5x': '5x',
      '6x': '6x',
      '7x': '7x',
      '8x': '8x',
      '9x': '9x',
      '10x': '10x',
      xs: 'xs',
      sm: 'sm',
      lg: 'lg',
    },
    '1x'
  );
  const spin = boolean('Spin', false);

  return {
    border,
    fixedWidth,
    flip,
    iconName,
    inverse,
    listItem,
    pull,
    pulse,
    rotation,
    size,
    spin,
  };
};

export default {
  title: 'UI|FontAwesomeIcon',
  decorators: [withKnobs],
  excludeStories: ['useKnobs'],
};

const Fai = ({ prefix }) => {
  const {
    border,
    fixedWidth,
    flip,
    iconName,
    inverse,
    listItem,
    pull,
    pulse,
    rotation,
    size,
    spin,
  } = useKnobs({ prefix });

  return (
    <FontAwesomeIcon
      border={border}
      fixedWidth={fixedWidth}
      flip={flip}
      icon={[prefix, iconName]}
      inverse={inverse}
      listItem={listItem}
      pull={pull}
      pulse={pulse}
      rotation={rotation}
      size={size}
      spin={spin}
    />
  );
};

Fai.propTypes = {
  prefix: PropTypes.oneOf(['fal', 'far', 'fas']).isRequired,
};

export const fal = () => <Fai prefix="fal" />;

export const far = () => <Fai prefix="far" />;

export const fas = () => <Fai prefix="fas" />;

System:
Please paste the results of npx -p @storybook/cli@next sb info here.

Environment Info:

  System:
    OS: macOS 10.15.2
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  Binaries:
    Node: 12.14.0 - ~/n/bin/node
    Yarn: 1.21.1 - ~/.yarn/bin/yarn
    npm: 6.13.4 - ~/n/bin/npm
  Browsers:
    Firefox: 70.0
    Safari: 13.0.4
  npmPackages:
    @storybook/addon-actions: 5.3.2 => 5.3.2
    @storybook/addon-knobs: 5.3.2 => 5.3.2
    @storybook/addon-links: 5.3.2 => 5.3.2
    @storybook/addon-viewport: 5.3.2 => 5.3.2
    @storybook/addons: 5.3.2 => 5.3.2
    @storybook/cli: 5.3.2 => 5.3.2
    @storybook/react: 5.3.2 => 5.3.2

Additional context
Add any other context about the problem here.

knobs question / support

All 4 comments

for anyone running into the same issues, this is how I solved it:

import React from 'react';
import { withKnobs, select, boolean } from '@storybook/addon-knobs';
import { FontAwesomeIcon, library } from '..';
import startCase from 'lodash/startCase';

export default {
  title: 'UI|FontAwesomeIcon',
  decorators: [withKnobs],
};

export const fal = () => {
  const border = boolean('Border', false);
  const fixedWidth = boolean('Fixed Width', false);
  const flip = flipSelect();
  const icon = iconSelect('fal');
  const inverse = boolean('Inverse', false);
  const listItem = boolean('List Item', false);
  const pull = pullSelect();
  const pulse = boolean('Pulse', false);
  const rotation = rotationSelect();
  const size = sizeSelect();
  const spin = boolean('Spin', false);

  return (
    <FontAwesomeIcon
      border={border}
      fixedWidth={fixedWidth}
      flip={flip}
      icon={icon}
      inverse={inverse}
      listItem={listItem}
      pull={pull}
      pulse={pulse}
      rotation={rotation}
      size={size}
      spin={spin}
    />
  );
};

export const far = () => {
  const border = boolean('Border', false);
  const fixedWidth = boolean('Fixed Width', false);
  const flip = flipSelect();
  const icon = iconSelect('far');
  const inverse = boolean('Inverse', false);
  const listItem = boolean('List Item', false);
  const pull = pullSelect();
  const pulse = boolean('Pulse', false);
  const rotation = rotationSelect();
  const size = sizeSelect();
  const spin = boolean('Spin', false);

  return (
    <FontAwesomeIcon
      border={border}
      fixedWidth={fixedWidth}
      flip={flip}
      icon={icon}
      inverse={inverse}
      listItem={listItem}
      pull={pull}
      pulse={pulse}
      rotation={rotation}
      size={size}
      spin={spin}
    />
  );
};

export const fas = () => {
  const border = boolean('Border', false);
  const fixedWidth = boolean('Fixed Width', false);
  const flip = flipSelect();
  const icon = iconSelect('fas');
  const inverse = boolean('Inverse', false);
  const listItem = boolean('List Item', false);
  const pull = pullSelect();
  const pulse = boolean('Pulse', false);
  const rotation = rotationSelect();
  const size = sizeSelect();
  const spin = boolean('Spin', false);

  return (
    <FontAwesomeIcon
      border={border}
      fixedWidth={fixedWidth}
      flip={flip}
      icon={icon}
      inverse={inverse}
      listItem={listItem}
      pull={pull}
      pulse={pulse}
      rotation={rotation}
      size={size}
      spin={spin}
    />
  );
};

function iconSelect(prefix) {
  const iconsArray = Object.keys(library.definitions[prefix]);

  return [
    prefix,
    select(
      'Name',
      iconsArray.reduce(
        (prefixes, prefix) => ({
          ...prefixes,
          [startCase(prefix)]: prefix,
        }),
        {}
      ),
      iconsArray[0]
    ),
  ];
}

function sizeSelect() {
  return select(
    'Size',
    {
      '1x': '1x',
      '2x': '2x',
      '3x': '3x',
      '4x': '4x',
      '5x': '5x',
      '6x': '6x',
      '7x': '7x',
      '8x': '8x',
      '9x': '9x',
      '10x': '10x',
      xs: 'xs',
      sm: 'sm',
      lg: 'lg',
    },
    '1x'
  );
}

function flipSelect() {
  return select(
    'Flip',
    {
      Horizontal: 'horizontal',
      Vertical: 'vertical',
      Both: 'both',
      None: null,
    },
    null
  );
}

function pullSelect() {
  return select(
    'Pull',
    {
      Left: 'left',
      Right: 'right',
      None: null,
    },
    null
  );
}

function rotationSelect() {
  return select('Rotation', { 90: 90, 180: 180, 270: 270, None: null }, null);
}

there's a bit of duplication, but I think its a decent implementation :)

Do you have a working example of this? I cannot seem to get font awesome icons showing in storybook.

@imcodingideas the example I provided is a working solution please follow the directions on creating an icon library using font-awesome.

https://www.npmjs.com/package/@fortawesome/react-fontawesome#build-a-library-to-reference-icons-throughout-your-app-more-conveniently

You're awesome. Thanks for replying. I converted my stories to CSF.

But I get:

Cannot convert undefined or null to object
TypeError: Cannot convert undefined or null to object
    at keys (<anonymous>)
    at Function.keys (http://localhost:6006/vendors~main.3d4df032770bbc1d0664.bundle.js:60949:24)
    at iconSelect (http://localhost:6006/main.3d4df032770bbc1d0664.bundle.js:3090:27)
    at push../src/app/library/components/Icons/icons.story.js.addSourceDecorator.__STORY__ (http://localhost:6006/main.3d4df032770bbc1d0664.bundle.js:3234:14)
    at decorated (http://localhost:6006/vendors~main.3d4df032770bbc1d0664.bundle.js:32753:14)
    at http://localhost:6006/vendors~main.3d4df032770bbc1d0664.bundle.js:20384:21
    at http://localhost:6006/vendors~main.3d4df032770bbc1d0664.bundle.js:21687:16
    at wrapper (http://localhost:6006/vendors~main.3d4df032770bbc1d0664.bundle.js:19956:12)
    at http://localhost:6006/vendors~main.3d4df032770bbc1d0664.bundle.js:20860:14
    at http://localhost:6006/vendors~main.3d4df032770bbc1d0664.bundle.js:20874:26

Font Awesome Works for me by itself

<FontAwesomeIcon icon={faPlusCircle} />

In your pull issue here I see you do something like import { FontAwesomeIcon, library } from '..'; but that doesn't work for me. This is what I have now:

import React from 'react';
import { withKnobs, select, boolean } from '@storybook/addon-knobs';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons';
import startCase from 'lodash/startCase';

export default {
  title: 'UI|FontAwesome Icon',
  decorators: [
    withKnobs,
    story => <div style={{ padding: '3rem' }}>{story()}</div>,
  ],
};

export const SimpleWorks = () => <FontAwesomeIcon icon={faPlusCircle} />;

export const CampaignFlagDoesNotWork = () => (
  <FontAwesomeIcon icon={['fal', 'browser']} size="lg" />
);

CampaignFlagDoesNotWork.story = {
  name: 'CampaignFlag (Does not work)',
};

function iconSelect(prefix) {
  const iconsArray = Object.keys(library.definitions[prefix]);

  return [
    prefix,
    select(
      'Name',
      iconsArray.reduce(
        (prefixes, prefix) => ({
          ...prefixes,
          [startCase(prefix)]: prefix,
        }),
        {}
      ),
      iconsArray[0]
    ),
  ];
}

function sizeSelect() {
  return select(
    'Size',
    {
      '1x': '1x',
      '2x': '2x',
      '3x': '3x',
      '4x': '4x',
      '5x': '5x',
      '6x': '6x',
      '7x': '7x',
      '8x': '8x',
      '9x': '9x',
      '10x': '10x',
      xs: 'xs',
      sm: 'sm',
      lg: 'lg',
    },
    '1x'
  );
}

function flipSelect() {
  return select(
    'Flip',
    {
      Horizontal: 'horizontal',
      Vertical: 'vertical',
      Both: 'both',
      None: null,
    },
    null
  );
}

function pullSelect() {
  return select(
    'Pull',
    {
      Left: 'left',
      Right: 'right',
      None: null,
    },
    null
  );
}

function rotationSelect() {
  return select('Rotation', { 90: 90, 180: 180, 270: 270, None: null }, null);
}

export const fal = () => {
  const border = boolean('Border', false);
  const fixedWidth = boolean('Fixed Width', false);
  const flip = flipSelect();
  const icon = iconSelect('fal');
  const inverse = boolean('Inverse', false);
  const listItem = boolean('List Item', false);
  const pull = pullSelect();
  const pulse = boolean('Pulse', false);
  const rotation = rotationSelect();
  const size = sizeSelect();
  const spin = boolean('Spin', false);

  return (
    <FontAwesomeIcon
      border={border}
      fixedWidth={fixedWidth}
      flip={flip}
      icon={icon}
      inverse={inverse}
      listItem={listItem}
      pull={pull}
      pulse={pulse}
      rotation={rotation}
      size={size}
      spin={spin}
    />
  );
};

export const far = () => {
  const border = boolean('Border', false);
  const fixedWidth = boolean('Fixed Width', false);
  const flip = flipSelect();
  const icon = iconSelect('far');
  const inverse = boolean('Inverse', false);
  const listItem = boolean('List Item', false);
  const pull = pullSelect();
  const pulse = boolean('Pulse', false);
  const rotation = rotationSelect();
  const size = sizeSelect();
  const spin = boolean('Spin', false);

  return (
    <FontAwesomeIcon
      border={border}
      fixedWidth={fixedWidth}
      flip={flip}
      icon={icon}
      inverse={inverse}
      listItem={listItem}
      pull={pull}
      pulse={pulse}
      rotation={rotation}
      size={size}
      spin={spin}
    />
  );
};

export const fas = () => {
  const border = boolean('Border', false);
  const fixedWidth = boolean('Fixed Width', false);
  const flip = flipSelect();
  const icon = iconSelect('fas');
  const inverse = boolean('Inverse', false);
  const listItem = boolean('List Item', false);
  const pull = pullSelect();
  const pulse = boolean('Pulse', false);
  const rotation = rotationSelect();
  const size = sizeSelect();
  const spin = boolean('Spin', false);

  return (
    <FontAwesomeIcon
      border={border}
      fixedWidth={fixedWidth}
      flip={flip}
      icon={icon}
      inverse={inverse}
      listItem={listItem}
      pull={pull}
      pulse={pulse}
      rotation={rotation}
      size={size}
      spin={spin}
    />
  );
};

Maybe Im missing something, key here?

Was this page helpful?
0 / 5 - 0 ratings