 Okej, började vi. Welcome to this session about component-driven closure script with storybook. My name is David and I'm a developer and I live and work in Stockholm, Sweden. Today I want to talk about storybook, give a quick overview and also how we can use storybook with closure script, writing stories and documentation. I want to highlight some issues or some things that can pop up when we work with tools from the JavaScript ecosystem. But I want to begin with component-driven. What is that? In short, it's about grouping relevant code into a component and combining components into features. And there's even a website for it, componentdriven.org. And here we can read about component-driven user interfaces. And about creating components, combining components into features, combining features into pages or views, and combining views into a full-blown webapp. So component-driven is about frontends and user interfaces. Storybook is a tool that can help us work component-driven. So we write stories which are isolated UIs where we can render and test our components. Storybook is an open-source tool, it's browser-based and it's all about JavaScript. And that means that we can use it in closure script too. And here is storybook in action. To the left we have a bunch of stories and in the center there is a canvas with a component. I have borrowed this component from material UI. And with storybook I can experiment and try out how a component behaves by using the storybook toolbar, like scaling up, scaling down. And this is my favorite. I can try out different viewports to see how a component behaves in different kinds of devices. And there's also a documentation section. I'll come back to that shortly. But there is more good stuff in the toolbar. There's a grid and there's also a feature where you can measure your individual components. And you can also outline the different things within a component. Different elements and things like that. So I'm going to try to write one myself. And I think a good place to start is to browse the storybook documentation. So I'm going to navigate to writing stories. And here we can read about the building blocks of a storybook story. And it's basically made of two parts. A default export and the story itself. And the default is information about the story, metadata. And the story is where we actually render the component that we want to build a story for. So I'm going to try to write this enclosure script. So I'm going to begin with the default. Adding a title like that. And this one needs to be exported. So it will be visible in storybook. Next up is the story. And that is a function like that. And that one also needs to be exported. So if we have a look in the storybook. Boom. I guess I forgot one important thing. And that is some JavaScript interrupts. So I have to convert my map into a JavaScript object. So now I have my hello world story, but it's empty. Because I don't have anything in my story function. So I'm thinking I want to add, I want to build a component. And I think I'm going to do that inline, in this file. In the real world I would have created a component in a separate namespace in the source folder and things like that. But for now I'm going to settle with this. So I'm going to do something simplistic. A string, maybe a header like that. And the next thing is to actually add that component to my story function. And I'm using reagent. I'm building react component using reagent. And the important thing to think about is that the reagent component needs to be wrapped in the as element function of reagent. And there is my hello world component like that. I think I forgot one thing in the metadata component. And I believe that is optional. But I also think that some third party tools rely on that key being set. So I better do that. And one thing to think about here is that you have to wrap this one in a reactify component from the reagent library. But this started to get some a bit too verbose. So I'm thinking about, I have actually created a helper function that will take care of all that boilerplate. Because I don't want to have to repeat myself each time I'm going to write a story. So I'm going to remove all these wrappers and let my helper take care of that. Let's have a quick look at the helper. So basically the helper takes the map and selects keys to run that reagent function on. So I'm going to continue with my component to make it a little bit more interesting. I'm thinking about passing in a message instead of hard coding that text like that. So I'm going to where I consume this component and I'm adding some text. Great. And I'm thinking about using a nice feature of storybook called arguments. And arguments is something that will pop up in this storybook UI. You can define defaults to an argument like that. And storybook will also send arguments to your stories. So you can work with them programmatically. And the args param is a JavaScript object. So we have to convert that one into a JavaScript, sorry, closure script map. So now I can extract the message key from my closure script params map. And if we have a look at the UI something should pop up right there. Great. So I have a textbox now. And that is my defined argument. So this means that I can experiment and try out components in the storybook UI. So I can experiment with different amounts of text to see how my component behaves like that. Great. You might have noticed the actions tab. The actions are similar to controls. But there's one difference. With actions you can handle events. So what I want to do is to continue working with my component. I'm thinking about adding a button to it. Like that and some text to it. And what I want to do, I want to attach an on click event handler. And I'm thinking about passing it into the component just like with the message. Something like that. And I think that this time I'm going to expect a map and I'm going to destruct the keys like that. Great. So next up, to make this working in storybook I'm going to use a feature called arg types. And that is how we define actions. So I give it a name. And what storybook will do, it will create an action. And an action is a function. It's a higher order function that will wrap our events. And the actions will also be passed in as arguments. So what I'm going to do now, I'm going to pass in the entire parameters map like that. And now we can see in the controls that I have an on click. And if I, I think I'm going to scale up the text a bit like that. And if we switch to the actions tab and click. You see that I have a registry click event. And it's currently the entire event displayed. But I'm going to do something more, a little bit more real world like and send some data through this event handler. I'm going to settle with the message. So let's click again there. So that's my data passed in. I'm going to change the text to make sure that storybook actually grabs the current, currently written text like that. Fantastic. Great. So that's actions. So let's continue with documentation. I know that quite a few use a storybook for documenting components. And the current view is quite sparse. I'm thinking about customizing it a bit. And that can be done by adding data to the default export. So I'm going to define parameters and docs. And what I want to do next is to add a description to this component. So that text pops up in the UI. And it turns out that this property does actually support markdown. So there's a couple of more properties that we can set. And that is the subtitle. Great. You might have noticed the show code button. Currently that doesn't look too well because I think this is one thing that doesn't work that well in the Closurescript JavaScript interop. But it is possible to customize this part too. So I'm going to add a string there instead. So it pops, turns out there. And what I have noticed is that the string has a format that looks like JSX or JavaScript. Like that. Storybook will actually render it nicely. But I'm going to do something more closury. Like that. Works pretty well too. I want to show you one thing more. I think that it's a good idea to put the code examples a little bit closer to where we have our implementation. So as an alternative we can actually grab the metadata from a component like this. Documentation is a feature that I personally don't use that much. I'm just going to remove this. But while I'm at it I'm going to take this even further because I did some digging in the Storybook source code and I found out about the addons docs feature. So I'm going to import it and grab a bunch of components and by doing that I can customize the entire view documentation view. I guess I had a typ out there, right? And that can be done by defining by writing custom documentation component using these Storybook React components. So I'm going to add a title, subtitle. Basically the similar thing that we see here but I'm going to customize it a bit. I'll come back to primary stories what these are. But I want to add a title first. Perhaps a subtitle too. And a short description. And to be able to display this I'm going to add a property to the default export. So it's the parameters key, docs and a page. And I'm going to set my custom component like this. So now the view renders my custom component. And primary in stories is about the stories itself. So I'm going to add a variation of my story and it will list all existing stories. And I can also customize the title of the stories part 2. Like that. So this is a way to use Storybook as a documentation. I'm going to reset all of this like that. Great. And I want to, yeah I forgot one thing. There's some documentation you can read about of course in the Storybook documentation. And there's also one thing called MDX. And that is something that I haven't tried yet. But if you do, please let me know how it goes and if it's useful for us as closure script developers. So now I'm going to leave this technical part of it and reflect a bit on the actual usage of Storybook. So I have created a component user card with a text box and a button and an avatar. And I can experiment how the different parts behaves. I'm getting a bit formal there if I change the text. What if I would have a really long name? You know Pippi Longstocking? This is her Swedish, full Swedish name. Longstrip. That's a really long name. So with this I can see how component behaves with unexpected texts. But when I think about it and press the outline I can clearly see that this component is made of several parts and perhaps it should be several components too. So what I have done I have extracted the avatar part so I can still experiment with the avatar features in isolation. What would happen if my gravitar link would be broken? Like that. It turns out that the material UI defaults are pretty good. If I'm going back to the user card there's a text box and I think there's some magical button appearing and disappearing too. I have actually extracted that one too into a component. So I can work with that one in isolation. And it turns out that this component has CSS that will write a line the entire components. That's why it appears to the right and also in storybook. And that strange save button I have actually extracted that one too so I can experiment with the fading in and fading out. The visual appearance of this button. So coming back to my original component there is now a combination of several components I can reflect on the actual usability. And even if there was a good idea when I started developing it I realized now that making that button disappear when I lose focus on the text box is a bad idea. It's a really bad idea. So I'm thinking about this, I can fix this. So I'm going to go to my new button component that I have extracted. Let's give that text a bit, yeah. So I'm doing some CSS magic styling with visibility. So I'm thinking about just deleting it like that. And you know deleting code is my favorite part of software development. Usually less code is less problems. So while I'm here I'm going to continue experiment with the UI of this button. What would happen if I increase the size to a medium? Maybe even large. And I cannot really remember what that variant property is doing. So I think I'm going to delete it because I have to read up on the material UI of it. Well I guess maybe I should undo that. And I think I can actually write contained to get a full background like that. Great. I think I want to size down the button to medium perhaps. Great. I'm happy. And let's make our linter seal jekondo happy too by doing that. Great. So all of this work. Well I'm going to fire up my preview so you can see the source code. So I have my components in a component folder in a variant folder. And I have my stories in a stories folder. And I'm using Shadow CLJS. And I have an alias for my app and an alias for my stories. So what differs them are the targets. The story target is an MPN module. I've also defined a regex to be able to find which parts of the code is actually a story. Like that. And in this case I put all my stories in the public folder. Perhaps in the real world maybe this code should be somewhere else. So with this closure script is done. So it's time to hand it over to Storibook. Storibook uses webpack and that is what I have been running all the time. Webpack listens to code changes and will recompile things. So there is also a storybook folder with some configuration. And here I tell storybook where to find the compiled stories and also how to customize storybooks webpack configuration because I use material UI and AWS amplify and between them there are some javascript quirks that needs to be taken care of. So that can be done in the webpack configuration. And this file is very important because storybook uses Babbel. But we don't need Babbel because closure script has already taken care of all that for us. So I'm telling storybook to just ignore Babbel. We can also set different backgrounds in the storybook UI itself like that. And if we have like global CSS or maybe even scripts it's possible to inject them into the stories by adding it to the preview. And here I can customize the behavior of storybook. Maybe how the documentation is rendered and what kind of viewports are displayed and things like that. Well, that's it. So I've been covering storybook how we can use it in closure script and how we can handle quirks from the javascript tooling ecosystem. Thank you for listening. I hope that you have learned something useful. And don't hesitate to contact me online. Maybe on Twitter, on GitHub, or maybe even on the Clojurian Slack. Thank you again for listening. Bye!