Redwood: Storybook support for cells

Created on 12 Mar 2020  ·  4Comments  ·  Source: redwoodjs/redwood

We (storybook team) think this is a really interesting project, and we'd love to help getting first class support for SB in the project (as mentioned in the README).

One thing that's especially interesting is the Cell files; you don't need to squint very hard to see they look a lot like story files (as pointed out by @sw-yx).

We've been thinking about it and we reckon someone could pretty easily write a babel plugin or webpack loader to compile a Cell file to a CSF file. This would be similar to the way that Storybook natively supports MDX by compiling it to CSF via a webpack loader.

That would mean that a user of Redwood could have a storybook setup where they just point SB at all their cell files and get a bunch of stories for free. That'd be pretty amazing! I'm sure there are lot more points we could integrate but this one seems like the most interesting (to me anyway).

Here's a bit of detail on how it might work:

If you take the example UsersCell.js file from the homepage:

export const QUERY = gql`
  query USERS {
    users {
      id
      name
    }
  }
`
export const Loading = () => <div>Loading users...</div>
export const Empty = () => <div>No users yet!</div>
export const Failure = ({ message }) => <div>Error: {message}</div>
export const Success = ({ users }) => {
  return (
    <ul>
      { users.map(user => (
        <li>{user.id} | {user.name}</li>
      ))}
    </ul>
  )
}

The compiler might compile it to something like:

export default {
  title: 'UsersCell',
}

const QUERY = gql`
  query USERS {
    users {
      id
      name
    }
  }
`
export const Loading = () => <div>Loading users...</div>
export const Empty = () => <div>No users yet!</div>
export const Failure = ({ message }) => <div>Error: {message}</div>
Failure.story = {
  args: { message: faker.string() }
};
export const Success = ({ users }) => {
  return (
    <ul>
      { users.map(user => (
        <li>{user.id} | {user.name}</li>
      ))}
    </ul>
  )
}
Success.story = {
  args: { users: generateMockFromQuery(QUERY) }
}

To implement the generateMockFromQuery() function we could use Apollo's query mocking code.

[Note that the syntax above uses the new args feature that's coming in SB6 and we are publishing an RFC about this week, but hopefully you get the idea!]


If the user wanted to write a set of specific stories (i.e. for different values of users in the success case), I guess a generator that generated a story file that just imported from the Cell and re-exported would make sense.

import { QUERY, Loading, Empty, Failure as FailureInput, Success as SuccessInput } from './UsersCell';

export default {
  title: 'UsersCell',
}

export { Loading, Empty };

export const Failure = props => <FailureInput {...props} />;
Failure.story = {
  args: { message: faker.string() }
};

export const Success = props => <SuccessInput {...props} />;
// User can override this or make multiple stories based on `SuccessInput`
Success.story = {
  args: { users: generateMockFromQuery(QUERY) }
}

Another option is the single-file components (that combine cells and stories) that @sw-yx mentioned, but I'm not sure of folks' appetite for that.

kinimprovement

Most helpful comment

First off, I wanted to say that we love Storybook! Thanks for putting so much work into it. It's a fantastic feeling to hear from you!

That would mean that a user of Redwood could have a storybook setup where they just point SB at all their cell files and get a bunch of stories for free.

Storybook is how we think people should be building and documenting components. We want to make the integration as smooth as possible.

Thanks for the direction and advice!

All 4 comments

First off, I wanted to say that we love Storybook! Thanks for putting so much work into it. It's a fantastic feeling to hear from you!

That would mean that a user of Redwood could have a storybook setup where they just point SB at all their cell files and get a bunch of stories for free.

Storybook is how we think people should be building and documenting components. We want to make the integration as smooth as possible.

Thanks for the direction and advice!

@tmeasday Wow, thanks for the suggestions! First class Storybook support is critical to the overall dream of Redwood app development, so it makes me really excited that you're interested in helping. We'll be spending some time addressing issues that are coming in from the community, but Storybook is high on the priority list for when things settle down a bit. We'll keep you in the loop, and if you have time to experiment in the meantime, we'd love if you shared what you learn!

Note: Webpack can not tree-shake static member assignments like Success.story = unless it is wrapped in if (process.env.NODE_ENV === "development").

I suggest a sibling file named UsersCell.story.ts that imports components from UsersCell.ts

I've been playing around with Storybook on a non-Redwood project, and one thing that's been really nice is pairing it with GraphQL mocking. Since we're embracing GraphQL, we should be prepared to automatically run a mocked GraphQL server on the same port / location as the normal one as part of our Storybook development flow (whatever would run when we type the imaginary command yarn rw sb/yarn redwood storybook)

Just something to think about when we do finally implement this.

Was this page helpful?
0 / 5 - 0 ratings