Design Systems with Storybook, v2

Creating Your First Stories

Steve Kinney

Steve Kinney

Design Systems with Storybook, v2

Check out a free preview of the full Design Systems with Storybook, v2 course

The "Creating Your First Stories" Lesson is part of the full, Design Systems with Storybook, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Steve creates primary and secondary stories for the button component. The meta object in a story creates the page for the stories and can provide default data passed to the props. Individual properties can be overridden in each story.


Transcript from the "Creating Your First Stories" Lesson

>> So with that, let's actually kind of jump in to the initial project that we have. Let's talk about how to actually write a story, right? Cool, So we've got this repo, it's based on a design system called anthology because I looked for synonyms for the story. I tried to come up with a cute name.

So we can pnpm, if you haven't already done that, I already have that in place or npm install. Cool, and you'll see it opens up on 6006 as well. And my biggest issue other than the fact that we still have the URL from the old Storybook is I don't have any stories in my Storybook, right?

So we should fix that, and so we can go ahead and do that. I feel like the initial first piece of any given component system is the button, right? And buttons are interesting, because on one hand, you're like a button. That's easy, I can make a button. On the other hand, this isn't even all of the buttons that we have in our design system in our app, there's also gradient buttons.

There's extra round deep buttons. There's buttons that have menus that poke out of them. There's buttons at the end of input fields. The number of buttons that you find in a design system is kind of insane, right? And then you add in dark mode, and next we're gonna work on high contrast mode and all of those things, and then you start to multiply that several times over.

The hardest one, the worst one is an input field, by the way. You think buttons have too many permutations, input fields are another fun one. We have some of those in this little mini design system as well, but I think the button is a really, really great case study to start out with here.

So let's go take a look, and I have some boilerplate things that we'll use later. But right now this is my button, right? There's no stories for this button. This is presently my button. It takes some props, which are currently set to any, and it spreads those props over the HTML button, right?

And so we have two missions here that we need to kinda do at the same time, which is one, actually make a real button. And two, we are going to start writing some stories around it. We'll do number two first, because that's the kinda theme we're working on today.

So the first thing we can do is we can create a new file. We can call it button.stories.tsx. Obviously, if you are using Svelte, it's not .tsx, or jsx, or what have you, that point is actually just ts or js. For vue, it is a .vue, so on and so forth.

The dot stories is the part that theoretically matters. One of the things that gets created when you run the npx storybook@latest net is it adds package.json, puts the scripts in there. It puts some initial stories that annoy me every time in there, so on and so forth. But it also creates some files in this .storybook directory as well, and these are your configuration files.

Out of the box, it will also see if you're using Webpack or Vite, it's pretty smart about setting everything up for you as well. So that .stories, right, theoretically, if you need a different extension or something along those lines, you can tweak and customize anything. This is just what it gives you by default starting out, which I mean, it's reasonable to me.

But if you are migrating from some other system or something along those lines, you can tweak this to your heart's content. So it's gonna look for any MDX files anywhere in our repository. And then anything that ends with .stories.pick your favorite file extension as you go along. So if you needed cjs in here for some reason, you could theoretically add that.

It also adds a few add-ons. So these come for free, theoretically. One or two of them, I think I added along the way. But add on onboarding, you can mostly get rid of once you start out. It's a helpful little tour as you go through. Essentials, that seems important.

Chromatic is the visual regression testing. The kind of relationship between chromatic and Storybook is Storybook is a completely open source, community-driven thing. Chromatic is a company that will definitely either on a free tier, which we'll use today or for money which I use, it work. Let you run the visual regression tests in the cloud and stuff along those lines.

They run effectively by the Storybook team, right? And so that's kind of symbiotic relationship, and so you get in that initial set for free. Interactions is where you saw that clicks and stuff along those lines. Themes and this accessibility are stuff that we're gonna kinda play around with as we go through, cuz we're good people, as well as contractually.

Need to have a high level of accessibility compliance, because of contracts and negotiations and stuff along those lines and themes so we can support dark mode along the way as well. Again, this framework thing will be different depending on how you set it up. If you run Webpack, you will get a Webpack version.

If you're using Ember, you'll see Ember, so on and so forth as we kinda go through. I turned off telemetry only because if somebody's watching this course later, I don't want them constantly nagged by, the update is available cuz they're on 8.09, right? I would leave this on normally, cuz updates are good, and you should know about them.

But that's just a little tweak that I have that I won't recommend that you follow, but just for the longevity of this course I've chosen to. Next, we have the preview.ts. The preview is that kind of UI that we've seen, that little kind of Canvas area. Any anything we put in this preview.ts, if you need to add scripts for Google fonts to the kinda head tag, you can do it in here.

For whatever unique things that you need for your application, we will make some tweaks in here, but this is kind of for any dependencies, any look and feel, and stuff along those lines happens in this preview.ts. Also anything that you want to kinda pass to every single story throughout your entire design system.

You can put in, here's kinda the global dependency injection place. There are other levels on the way down that we'll see as we begin to implement some stories. But this is kind of, hey, we need to inject the URL for the mock server in test or development and then have an environment variable switches to production for our production Storybook, right?

Any kind of that stuff that needs to happen globally throughout your Storybook happens in here as well. Sorry, we got all that set up. So then we've got this .stories file here as well. And we, like I said before, are gonna support TypeScript. So there's a little extra ceremony that we need to do, but for great advantage, right, and we'll kinda see that in a second.

And so we will import some type as one of the first things that we do. So we'll put import type, and we're gonna import two of them, Meta and is a StoryOb. These are types that are kind of unique to just getting us effectively IntelliSense. We're doing the typescript in this case, because IntelliSense is great, and it will let us know if we pass the wrong argument in one of these stories, so on and so forth, and that isn't Storybook React.

If you are using a different framework, obviously, it's gonna be a little bit different. Bbut then what we can do next is there is the individual stories, and then for the collection of stories, the kind of meta, right? What is the name of this story? What component are we using, so on and so forth?

And those are shared across all the stories, you can override them at any given point. So we'll also import our very cool button component, which is totally useful at this point from./button. Cool, and so we've got that in place. So now, we have this Idea of the meta that we wanna have.

So we have this const meta, and that's going to then take a title, which we're gonna start out as button. One of the things that I will talk about later is there are ways that you can name the titles of your stories that influence kind of the organization hierarchy of your Storybook, right?

And so much like something like Figma, using a slash or something along those lines will also kind of create a hierarchy as we go along. But we're gonna start out real simple and not have too much of a hierarchy for starters, and we'll say component is, in this case, the button, right?

This is kind of the bare minimum that you need. I'm gonna say that it satisfies the type of meta to make sure that I've actually hit all the things. And what this will do is, as you add stuff to the meta for this collection of stories, it will make sure that you're actually passing it in the correct options, right?

Especially, this is helpful. If either A, mostly, you're coming from a previous version of Storybook, and you're like it used to be like this, and you copy and paste old code you're in the process of migration. This will help you see like, that's not actually valid anymore and tweak stuff along the way.

But we're not gonna do any options just yet. The other thing we need to do is we export that meta as the default, right? And if you're using just JavaScript, you could theoretically do export default in this object. The only thing we need access to it for is to make one more type, which is the type Story.

And that's going to be this StoryObject and the type of button in this case. Cool, All right, so we've got all of this in place. And so now, we've got our initial story. One of the cool things about Storybook 7, and now 8 is that it uses effectively just ES modules, right?

And so the ES modules that you export that are not the default export are your stories. So the world's simplest story in this case is we can do this export const Primary, we'll keep that the type Story. And the reason we did this up here is that it actually helps us with making sure that we've got the right objects in here.

So we've got that in place, and let's just start with this. And we're gonna have a slight problem that we'll deal with in a moment, but this is the world's simplest story, and we'll see that it shows up now in our Storybook. We no longer have the problem of no stories in our Storybook, cuz we have one of them.

And you can see the implicit hierarchy here is, we've got this meta idea of buttons, right, and then we've got this primary button. And if you export one called secondary, right, default with a capital D, because default with a lowercase d means things, those will also show up here as well.

If you use camel case, you'll get spaces for free as you kinda go along here as well, and we can close this pane for a little bit. My big problem with the button right now is that there's a button, it's got nothing in it, all right? And so just to verify our own sanity here, if I went into this buttont.tsx and said, Instead of self-closing, we'll say /button.

Cool, and now you can see that it is a button, right? And that's all it takes to write a story. Now, obviously, there's some nuance and some things that we can add in here. You'll notice that other than passing meta, the reference to the button, right, there's not a lot of React in here.

And so one of the things I think Storybook, it was beta and Storybook 6. And then it was really reels and Storybook 7 and really, really, really reels in Storybook 8 is what's called component story format. What I'm writing in React looks almost exactly the same im Svelte with very tiny differences in so far that's Svelte uses slots and React uses children.

I mean, just a very minuscule differences between the framework show up in here, but generally speaking, I don't actually have to write the component. This becomes theoretically the props that I might pass them will be args and if I took a prop, a variant primary, that is a prop that would get passed in, right?

If I did, let's say, such as I do style is, Sure, once the object is it, that part is it is passing it to React, so you have to keep that in mind. So backgroundColor blue, and that becomes a prop that gets passed in, right? And so like I said, completely agnostic.

And the powerful thing about it being an object is you get a certain amount of composability, right? If you have one set of default props that you wanna use and you wanna use the spread operator, it's just JavaScript. You can use a spread operator, you can do whatever you want.

If you need an escape hatch for some reason, though, right, where you're like, this is great, I wanna use this mozadime, I have a reason that I actually wanna write some JSX, or what have you. You can use the component story format, or you can use another option called render, and render will actually just let you kind of give it a function and, It'll actually render that instead.

Why are you angry? So then we have our button once again. And so yeah, all of that is kind of in place. You can render, you can use your work predominantly, and especially in the spirit of staying framework-agnostic. We're gonna stick mostly with the component story format, except for the one or two unique cases where we choose to render something as we go along.

So yeah, that is our kind of first story. And again, if we wanted to write a second story, right, export const, a secondary button comes another story. And let's say, for instance, we wanted to use the, it's args children. See, this is why the TypeScript is helpful. It's like, that's not how this works.

How you talk and type at the same time, neat. And so now, if you look, we've got two stories. I overwrote it in mind, so let's go back and change that to the way that it was. So that once got the child prop passed in, this one doesn't, right?

And that is kind of the beginning steps. The other thing that you'll see as I open the sidebar here, it has figured out that this does take a prop called children. One of the cool things for React and Vue, I think the other ones you don't get as much help here.

It'll definitely know about any prop that you use, but if you are using TypeScript or JSDoc, either one is fine. And you're using React or Vue, it will either use React.gen, or I think the Vue one is called Vue.gen. I don't use Vue, so I'm doing that from memory.

And it will do a static analysis of the code that you're importing and try to acquiesce what the props are, right? And so even if you're not using the props, it'd be like, I saw the types that you use for the props React component, and it'll automatically fill this out and make some best guesses on what controls to use.

In a little bit, we'll see how we can customize those controls, everything along those lines, but we do kind of wanna begin to style this button up a little bit. Cool, so you know what might be really annoying that I don't wanna do? You notice how my primary button still has no content and my secondary button does, right, it would be great if I didn't have to type this for each and every one of them, right?

So we can do that, and we'll do this a lot more to great effect in a little bit. But I think this begins to set up the understanding of the hierarchy of how a lot of the settings. So anything in that preview.ts that we saw earlier, that is across every story in throughout your entire Storybook, right?

Anything I put in an individual story is unique to that individual story, right? This is great for if you wanna have different props to different stories. But for the things of, hey, I wanna pass in just the same prop to all of these, right, that's where this meta comes into play, right?

So this will be shared across every story in this .stories file. Cool, so let's go ahead and we'll do that. And so here we'll say args, and we'll say that children, button seems like a good enough name. If somebody wants to change their mind on this, they can.

That's why you have your own copy of the repo. Cool, and so now, you'll notice this one I left empty. This one has children, so it's gonna work the way we think it's gonna work, which is this one then goes back up to the meta default. This one, you can override that prop with whatever you wanted along the way and both work just as well, and you start to get a sense of the kind of the way that inheritance works in Storybook as well.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now