Quick Reminders
This article follows my previous one. If you haven't read it yet, you can do so here: Symfony Can Help You Fall in Love with Your Front-End Team.
To give you context, we are developing a small application that lets you listen to various live radio stations.
We've adopted the principles from Component Architecture and, naturally, after designing various components of differing sizes and complexity, we are beginning to establish what is known as a design system.
Overwhelmed by All These Components?
It's common to feel overwhelmed as the number of components grows. In a team setting, or when developing new features, it's crucial to know what components are already available. When we create a new component, we want to share it immediately with our teammates.
So, how do we manage this?
Documenting everything? Yes, but it's time-consuming, and let's face it, we're developers—we tend to be a bit lazy.
But here’s some good news: there’s already a solution in the JavaScript world called Storybook!
What is Storybook?
Storybook is an exceptional tool for documenting components. It's specifically designed for this purpose, but it offers much more. It allows us to build components in isolation, test them, and mock them to see how they appear in different states or create specific end-to-end scenarios for each component, among many other features! And if you're still not convinced, just look at how beautiful your documentation can look with Storybook:
Here you can see an example of how I documented our Alert component. This documentation helps my teammates understand how to use the component and the different variations available.
Integrating Storybook with Symfony
Thanks to contributions from the community, you can now use the StorybookBundle for Symfony, developed by my friend Nicolas (not Grekas). I’m very excited about this release—not only because it involved a lot of work, but also because this bundle demonstrates that Symfony can match any front-end framework in capabilities.
To get started, install the bundle with Composer:
**composer require sensiolabs/storybook-bundle**
Then you can use the init command to configure everything for you.
bin/console storybook:init
Don’t run away! We gonna use npm but just to run the storybook. This will not have any impact to your project. And the bundle have an integration with AssetMapper, npm is just to start the storybook.
npm install
And to finish
npm run storybook
And just like that you are fully set up! You can go to: http://localhost:6006/ (it can be something else if your port is not available) to admire your storybook.
Write your first story
Let's get started! The first thing we want to do is show our Alert component in our Storybook.
import Alert from '../templates/components/Alert.html.twig';
import { twig } from '@sensiolabs/storybook-symfony-webpack5';
export default {
component: (args) => ({
components: {Alert},
template: twig`
<twig:Alert color="{{ color }}" size="{{ size }}" position="{{ position }}">
{{ message }}
</twig:Alert>
`
}),
argTypes: {
color: {
options: ['blue', 'red', 'green'],
control: {type: 'radio'},
},
size: {
options: ['lg', 'md', 'sm'],
control: {type: 'radio'},
},
position: {
options: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
control: {type: 'radio'},
}
}
}
export const Default = {
args: {
color: 'blue',
size: 'sm',
position: 'top-left',
message: 'Hello world!'
},
}
Let me explain what we have here:
import Alert from '../templates/components/Alert.html.twig';
We import our component template. Yes, this is Twig code, but thanks to the bundle, it works!
import { twig } from '@sensiolabs/storybook-symfony-webpack5';
Then we import a twig
function that allows us to write Twig code directly in our JavaScript.
export default {
component: (args) => ({
components: {Alert},
template: twig`
<twig:Alert color="{{ color }}" size="{{ size }}" position="{{ position }}">
{{ message }}
</twig:Alert>
`
}),
argTypes: {
color: {
options: ['blue', 'red', 'green'],
control: {type: 'radio'},
},
size: {
options: ['lg', 'md', 'sm'],
control: {type: 'radio'},
},
position: {
options: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
control: {type: 'radio'},
}
}
}
Here is everything that is common to all stories related to our Alert component.
We have the component
key that allows us to define our component, the Alert, and a template. We use the twig
function, and everything inside can be any valid Twig code we want. Here, we simply render the Alert component and give this component props: color
, size
, and position
.
Then we define some argTypes
that allow us to describe the controls we want under the story.
Then we define our first story:
export const Default = {
args: {
color: 'blue',
size: 'sm',
position: 'top-left',
message: 'Hello world!'
},
}
And just like that, we have the following result!
You can play with the controls to test your component with different variations.
Then we can add more stories:
export const Large = {
args: {
color: 'blue',
size: 'lg',
position: 'top-left',
message: 'Hello world!'
},
}
export const Error = {
args: {
color: 'red',
size: 'lg',
position: 'top-left',
message: 'Hello world!'
},
}
export const Success = {
args: {
color: 'green',
size: 'lg',
position: 'top-right',
message: 'Good job!'
},
}
So now, if we look at the menu of our Storybook, we have three stories:
This is great, but we can go a bit further. Add the tags: ['autodocs']
to your export default
:
export default {
component: (args) => ({
components: {Alert},
template: twig`
<twig:Alert color="{{ color }}" size="{{ size }}" position="{{ position }}">
{{ message }}
</twig:Alert>
`
}),
...
tags: ['autodocs']
And just by adding this tag, Storybook creates a documentation page for us:
And if that's not crazy enough, you can create a .mdx file:
import {Canvas, Controls, Meta, Story} from "@storybook/blocks";
import * as AlertStories from "./Alert.stories";
<Meta of={AlertStories} />
# Alert
An Alert display information to the user.
<Story of={AlertStories.Default} />
Here are the properties you can use on the alert component:
<Controls of={AlertStories.Default} />
## Error
For error message you can use:
<Canvas of={AlertStories.Error} />
## Success
For success message you can use
<Story of={AlertStories.Success} />
As you can see in this .mdx file, we were able to import our stories into our documentation to make everything playable.
This is really cool! But you know what? This is only the beginning! We used Storybook to document a simple component, but Storybook can do much more. It can help with developing complex components or testing your components. We still have a lot to learn with Storybook, so stay tuned for the next article!
Thanks for reading. See you soon!
Top comments (3)
This was very informative! How would you handle documenting more complex components in Storybook? Would love to hear more about that in a future post!
Thanks for your feedback I will write a new article soon to answer your question 😁
Storybook + Twig components seem like an awesome match !
Symfony "ux bundles" encourages distributing Twig Components through bundles for variious Symfony apps to re-use. I'm wondering how we can leverage Storybook from within the bundle, since the bundle repo is where the development and bug-fixes happen. In the bundle repo, we have no symfony app to serve storybook pages...