Storybook: Adding stories from data structure throws error

Created on 14 Apr 2016  路  8Comments  路  Source: storybookjs/storybook

I have a working set of stories from the sample code. However, when I try to populate stories from a simple data structure, storybook gives me an error. Loading stories from data would keep code DRY when you are sharing fixture data between stories and unit tests.

When I modify the storybook example components/stories/TodoItem.js with the following trivial change, it gives me the following error preview.js?a230:61 Uncaught TypeError: story is not a function. Is there a better way to do this?

const data = {
  'not completed': { id: 'the-id', text: 'Hello Todo', completed: false },
  'completed': { id: 'the-id', text: 'Hello Todo', completed: true }
}

const todoStories = storiesOf('TodoItem', module)
for (const key in data) {
  todoStories.add(key, getItem(data[key]))
}

// storiesOf('TodoItem', module)
//   .add('not completed', () => {
//     const todo = {
//       id: 'the-id',
//       text: 'Hello Todo',
//       completed: false
//     };
//
//     return getItem(todo);
//   })
//   .add('completed', () => {
//     const todo = {
//       id: 'the-id',
//       text: 'Hello Todo',
//       completed: true
//     };
//
//     return getItem(todo);
//   });
discussion

Most helpful comment

Hi @shilman,
I believe the code given above will work fine if we use an arrow function.

const todoStories = storiesOf('TodoItem', module)
for (const key in data) {
  todoStories.add(key, () => getItem(data[key]))
}

React storybook can be modified to support data structures by changing these 2 lines.

But using a function to create the component can be useful. Let's keep the issue open for now.

All 8 comments

Hi @shilman,
I believe the code given above will work fine if we use an arrow function.

const todoStories = storiesOf('TodoItem', module)
for (const key in data) {
  todoStories.add(key, () => getItem(data[key]))
}

React storybook can be modified to support data structures by changing these 2 lines.

But using a function to create the component can be useful. Let's keep the issue open for now.

@mnmtanish You're correct, it's just a bug in my code, not a problem with storybook. Thanks for the quick response and solution! I think we can close the issue.

You're welcome 馃槂

@mnmtanish Could be a convenience function perhaps, something like todoStories.addAll(data)? Not sure if it's a good idea, but it's a thought 馃槃

Could be a convenience function perhaps, something like todoStories.addAll(data)?

I was thinking this as well. The only problem though is that currently the add() story requires you to pass a function that does the rendering. addAll could only work like you've described if a Story was set-up knowing what component it would render, and then only allowed you to pass props.

Perhaps something like:

todoStories.addAll(data, (props) => <TodoItem {...props} />);

Where the render function of addAll passes the props each time. Could actually work quite well with functional components:

// functional component
const TodoItem = ({ id, text, completed }) => <div>{id}: {text} {completed ? 'Y' : 'N'}</div>;

// ... other set-up

// Since it would be called, given the props.
todoStories.addAll(data, TodoItem);

This looks great. Mind if I submit a PR, to practice my "open-sourcery"?

I don't think anyone will stop you :smile:

One thing I notice though is that add(kind, fn) requires you to give it a name (_kind_) and a render function that does all the work. Where this proposed addAll(data, fn) requires a kind/props pair, with a render function that accepts props. While it might work well, add and addAll work quite differently, so it might be weird that they are named so similarly.

Looking into the code it's a non-trivial change. I think it's better implemented as a convenience function external to the library. This works for me for now:

function addAll(stories, storyData, componentFn) {
  Object.keys(storyData).forEach((storyName) => {
    stories.add(storyName, () => componentFn(storyData[storyName]))
  })
}
addAll(todoStories, todoData, TodoItem)

Though I guess this only works for functional components. Anybody know how to write a generic function to render a class-based component?

todoStories.addAll(data, TodoItem);

1209 Will be considered for api-v2

Was this page helpful?
0 / 5 - 0 ratings