Visual testing of React components with StoryBook
While developing a web application, we often receive requirements to build a new component without knowing where it will be used.
What many people end up doing is creating a test page just to have something to show to stakeholders.
A better alternative is to use StoryBook. This tool allows isolated testing of web components and is available for React, Vue, and Angular, among others.
The tool must be installed in an existing project. In the root of the project, just run:
1npx sb init
During the installation process, an analysis of the project and its configuration is performed to install the most suitable dependencies for the project.
After the installation is complete, we have a new script to run our Storyboard:
1npm run storybook
By default, StoryBook will be reachable at http://localhost:6006
In the project structure, a new folder is created with some sample stories:
The component we will test is an email entry form:
1import react, { useState } from "react";
2import css from "./EmailForm.module.css";
3
4export interface EmailFormProps {
5 initialValue: string;
6 message: string;
7 onSubmit: (email: string) => void;
8};
9
10export const EmailForm = (props: EmailFormProps) => {
11 const [email, setEmail] = useState(props.initialValue);
12 const emailParts = countEmailParts(email);
13 return (<section className={css.container}>
14 <div className={css.spectrum} aria-hidden>
15 {Array.from(Array(5)).map((_, i) => (<div className={i + 1 <= emailParts ? css.barActive : css.bar} key={i}></div>))}
16 </div>
17 <header className={css.header}>
18 <h2>{props.message}</h2>
19 </header>
20 <input
21 className={css.email}
22 type="email"
23 placeholder="your email"
24 value={email}
25 onChange={(evt) => setEmail(evt.target.value)}
26 />
27 <button className={css.submit}>Sign up</button>
28 </section>)
29}
30
31const countEmailParts = (email: string): number => {
32 if (/@.+\..{2,}$/.test(email)) {
33 return 5
34 } else if (/@.+\..?$/.test(email)) {
35 return 4
36 } else if (/@.+$/.test(email)) {
37 return 3
38 } else if (/@/.test(email)) {
39 return 2
40 } else if (/.+/.test(email)) {
41 return 1
42 } else {
43 return 0
44 }
45}
Component tests are created in a file that follows the pattern \*.stories.tsx (or .jsx if we are working directly with JS)
1import { Story, Meta } from '@storybook/react';
2import { EmailForm, EmailFormProps } from "../components/Email/EmailForm";
3
4export default {
5 title: 'Components/EmailForm',
6 component: EmailForm,
7} as Meta;
8
9const Template: Story<EmailFormProps> = (args) => <EmailForm {...args} />
10
11export const Empty = Template.bind({});
12
13Empty.args = { initialValue: "", message: "SUBSCRIBE!!", onSubmit: (e) => { } }
14
15export const OnePart = Template.bind({});
16
17OnePart.args = { initialValue: "pedro", message: "SUBSCRIBE!!", onSubmit: (e) => { } }
18
19export const TwoParts = Template.bind({});
20
21TwoParts.args = { initialValue: "pedro.roque@", message: "SUBSCRIBE!!", onSubmit: (e) => { } }
22
23export const ThreeParts = Template.bind({});
24
25ThreeParts.args = { initialValue: "pedro.roque@outlook", message: "SUBSCRIBE!!", onSubmit: (e) => { } }
26
27export const FourParts = Template.bind({});
28
29FourParts.args = { initialValue: "pedro.roque@outlook.", message: "SUBSCRIBE!!", onSubmit: (e) => { } }
30
31export const FiveParts = Template.bind({});
32
33FiveParts.args = { initialValue: "pedro.roque@outlook.com", message: "SUBSCRIBE!!", onSubmit: (e) => { } }
In the story definition, we start by creating the metadata, which will be used to present the story on the StoryBook page.
Next, we create the template of the component we will test.
Finally, we export the scenarios we want to test, defining the component's props for each one.
On the StoryBook page, we can now see the rendering of the component:
This way it becomes much easier and faster to create something that can be shown to stakeholders, making the feedback cycle quicker and more effective.