adjoe Engineers’ Blog
 /  Frontend  /  Writing Stories with Storybook to Visualize React Dashboard Components
decorative purple image with colorful elements
Frontend

Writing Stories with Storybook to Visualize React Dashboard Components

In my last article, I explained how your codebase should look in order to seamlessly integrate and use Storybook to document and interact with your components. I also took you through the process of how we integrated Storybook in our new dashboard project, explained how to solve common integration problems, and demonstrated how to automatically upload your documentation to Chromatic, Storybook’s own hosting service.

In this article, I’ll explain the process of writing stories with Storybook for both simple components, like buttons, and more complex state-based components, like filters and dropdowns.

What Is a Story?

When we talk about writing stories with Storybook, a story describes and visualizes a component’s specific state. A component can have multiple stories, based on which states the component can support. Different stories are normally used to show how the component transforms based on which properties are given to it. 

Screenshot of a button story with four substories in Storybook in article about writing stories with Storybook

A story itself has two views: Canvas and Docs.

The Canvas view shows the single story with controls below it. With these controls, you can set all of the component’s properties. This view can also be used to interact with the component. You can change the size of the page to see media queries in action, zoom in and out, switch to dark mode, show the grid, and more.

 The Canvas view also supports various add-ons you can install for your Storybook. You can find a ton of different add-ons here.

Screenshot of the Canvas page of a Button story in Storybook

The Docs view is the documentation page of the component.

Here, you can view the written description of the component. You’ll see a list of all supported properties, as well as prop comments set in the code to explain these. 

The Docs view will show all stories on one page, and you can also view code examples for integration.

Screenshot of the Docs page of a button story in Storybook
Screenshot of the substories in the Docs page of a button story in Storybook
Screenshot of the code example of a button story in Storybook

How Do I Write a Story for a Simple Component?

Storybook provides you with a clear structure of how a story should look like.

The story exports a default object typed as a ComponentMeta, which includes the field’s title and component. The title is responsible for the placement of the story in your Storybook. A title string “Common/Button” will create the component story under the folder “Common” entitled “Button.” In the component field, you pass the actual component to render as a React ComponentType.


Then you can create a template, which is the actual component-rendering function. It will return JSX, which will be used to render the component in the story, passing the properties as arguments. This template can return anything you want and is also where you’ll add the necessary logic or state for more complex components later on. For example, you could wrap your button component in a box here and give it a different background, if you want.


Lastly, the actual stories will be created by binding your template to ComponentStory constants.With this, you can create any number of stories for your component and change the arguments of the template according to your needs.

Let’s use a simple component as an example – a simple wrapper of the MUI button.

interface IButtonProps {
 variant: 'text' | 'outlined';
 onClick: () => void;
 children: React.ReactNode;
}
export const MyButton: FC<IButtonProps> = ({ variant, onClick, children }) => (
 <Button variant={variant} onClick={onClick}>
   {children}
 </Button>
);

Out of this component, you can easily create two stories that show different variants.

First, create a new file next to the component and name it “Button.stories.tsx.” Then, create your default export.

export default {
 title: 'Common/Button',
 component: MyButton,
} as ComponentMeta<typeof MyButton>;

In TypeScript, you have to make sure that you type the object correctly and add the typeof component as a generic.

Next, add the template. In this case, it renders the button with a simple text as the child and passes the arguments to it with prop spreading.

const Template: ComponentStory<typeof MyButton> = args => (
 <MyButton {...args}>Button</MyButton>
);

And then you can define your stories with bindings by setting the variant in the args to what you need. The onClick definition is in this case not necessary – you can just assign a function that does nothing, or you could make a console log.

export const DemoOutlined = Template.bind({});
DemoPrimary.args = {
 onClick: () => {},
 variant: 'outlined',
};


export const DemoText = Template.bind({});
DemoSecondary.args = {
 onClick: () => {},
 variant: 'text',
};

How Do I Write Stories for Complex Components?

While simple components, like buttons, do not need any logic to visualize them with stories, more complex components – for example, a select component – will need both a state and state change logic in order to function correctly in the story. In this case, you will need to expand the template with values, state, and change listeners.

Let’s take this simplified select component as an example.

export interface IMySelectProps {
 label?: string;
 values: { name: string; value: string }[];
 selectedValue: string;
 onChange: (event: SelectChangeEvent<string>) => void;
}


export const MySelect: FC<IMySelectProps> = ({
 label,
 values,
 selectedValue,
 onChange,
}) => {
 return (
   <Select value={selectedValue} label={label} onChange={onChange}>
     {values.map(value => (
       <MenuItem value={value.value} key={value.name}>
         {value.name}
       </MenuItem>
     ))}
   </Select>
 );
};

This component doesn’t function on its own; it requires logic on its parent component in order for the user to see and select options. 

Let’s now create a story for it.

First, create a new file called “MySelect.stories.tsx” next to the Select Component file. Then, like in the button story, create the default export object.

export default {
 title: 'Common/Select',
 component: MySelect,
 argTypes: { onChange: { action: 'onChange callback was called with' } },
 args: {
   label: 'Fruit Select',
   values: [
     { name: 'Banana', value: 'banana' },
     { name: 'Lemon', value: 'lemon' },
     { name: 'Orange', value: 'orange' },
   ],
 },
} as ComponentMeta<typeof MySelect>;

You can add two new fields next to the title and component fields used previously.

argTypes lets you set argument options for your story manually. Here, you can add descriptions for the arguments and, like in this case, add an action to it. The action will automatically trigger in the action section of the canvas, and will output the defined string together with the function’s passed parameters.
In args, you can set default arguments that do not change per story. In this case, add the label and values, so it will be set for all of this component’s stories.

Next up, create the story template.

const Template: ComponentStory<typeof MySelect> = args => {
 const { onChange, values } = args;
 const [value, setValue] = useState('banana');


 const onSelectChange = (event: SelectChangeEvent<string>) => {
   onChange(event);
   setValue(event.target.value);
 };
 return (
   <MySelect selectedValue={value} onChange={onSelectChange} values={values} />
 );
};

First, First, you need onChange and values, which you get from args by destructuring.

Create a state, which will save your current selected value as a string, with a default value. 

In order to trigger the action correctly, you need to call the onChange function of the args. Then you can set the state to the selected value from the component.
Instead of spreading the args, assign the state value, the custom change function, and the values from the default args.

Finally, create a simple demo to show the story in Storybook.

export const Demo = Template.bind({});

The result is a fully functioning select dropdown component you can try out in your Storybook.

Screenshot of the dropdown component in Storybook

What’s Next?

You should now have the tools and knowledge to create your own visual stories for your components – regardless of whether they function without external logic or whether they need additional logic in the parent.

After discussing the process of writing stories with Storybook, read my third and final article. I’ll be showing you how to utilize Storybook’s features of documentation to document your components, as well as give you a brief overview of Chromatic’s visual-regression testing tool.

Client Solutions Lead (f/m/d)

  • adjoe
  • Playtime Supply
  • Full-time
Every great app out there deserves to be connected with the right users and the right revenue streams. And adjoe will give this to them.

adjoe is a leading mobile ad platform developing cutting-edge advertising and monetization solutions that take its app partners’ business to the next level. Our unique ad unit Playtime has made us one of the fastest-growing ad platforms and top-ranking user acquisition sources for app publishers worldwide (ranked #1 for growth in the AppsFlyer Index). And that’s just the start: We’ve just launched our programmatic in-app platform in order to disrupt the status quo of mobile ad monetization. Again. 

Home to an advanced tech stack, powerful financial backing from Bertelsmann, and a long-term growth mindset, adjoe is part of the AppLike Group ecosystem. A hub of disruption and thought leadership in the app economy, with a driven and dynamic workforce to be reckoned with. 

Be the next tech-driven, ambitious talent to join our growing teams!

Meet Your Team: Customer Service

Big ambitions lead to a big impact – and big revenue numbers. 

Your role will be to lead and build a Global Customer Service Organization that services both our business partners as well as our B2C customers. You will be reporting into the CRO and align with your peers in New Business and Account Management in order to provide the best technical support and service to our publisher partners and advertisers. 
What You Will Do:
  • Manage a team of Technical Account Managers. They ensure the technical onboarding of our newest app partners and a smooth SDK implementation process. 
  • Manage a team of B2C support agents. They are problem solvers for our mobile gamers, who have technical issues related to our partners’ games.
  • Become the link between various stakeholders, teams, and clients to provide solutions to maximize their business goals. 
  • Evolve our service organization based on our geographical expansion as well as service and organizational needs arising from growing business with Enterprise clients. 
  • Ensure a consistent level of knowledge and quality in the execution of your teams.
  • Collaborate with the product and technical teams to ensure our platform meets client needs and provides a positive user experience.
  • Ensure up-to-date product and technical documentation in alignment with our product team. 
  • Be part of an international English-speaking management team dedicated to scaling our adtech platform beyond our hundreds of millions of monthly active users.
  • Who You Are:
  • Minimum 3 years of experience in a leadership role within a service organization, preferably in the (mobile) ad tech industry.
  • Proven track record of building and managing high-performing teams.
  • Strong analytical and problem-solving skills with the ability to work with data –  use SQL to query databases, Excel and other analytics tools
  • Ability to build and maintain strong relationships with clients and colleagues.
  • You thrive in a team environment and are an excellent cross-team collaborator with excellent analytical and communication skills
  • Basic Knowledge of HTML and JavaScript or one basic programming or scripting language 
  • You have a working knowledge of how different technical systems communicate with each other, such as being able to test HTTP REST endpoints and understand what could go wrong when they don’t work.
  • Plus: You are familiar with Java/Kotlin/Swift or have experience building your own apps.
  • Plus: Understanding of the mobile ad tech landscape and current best practices.
  • Heard of Our Perks?
  • Work-Life Package: 2 remote days per week, 30 vacation days, 3 weeks per year of remote work, flexible working hours, dog-friendly kick-ass office in the center of the city.
  • Wealth building: virtual stock options for all our regular employees.
  • Relocation Package: Visa & legal support, relocation bonus, reimbursement of German Classes costs and more.
  • Happy Belly Package: Monthly company lunch, tons of free snacks and drinks, free breakfast & fresh delicious pastries every Monday
  • Physical & Mental Health Package: In-house gym with personal trainer, various classes like Yoga with expert teachers.
  • Activity Package: Regular team and company events, hackathons.
  • Education Package: Opportunities to boost your professional development with courses and trainings directly connected to your career goals 
  • Free of charge access to our EAP (Employee Assistance Program) which is a counseling service designed to support your mental health and well-being.
  • Build our signature product

    See vacancies