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

Writing Stories with Storybook to Visualize React Frontend 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.

Technical Account Manager (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).

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. 

Our US HQ launched in the beginning of 2023. Since its inception the US office has played a critical role in adjoe’s massive year-over-year growth, and we’ve continued to grow our investment in the US as a key source of future growth. Be the next tech-driven, ambitious talent to join our growing US team!

Meet Your Team: Playtime Supply

Playtime Supply is building a unique software solution for app and game developers that puts users’ needs first. Instead of traditional monetization mechanisms, Playtime builds an engaging ad experience in which users can play and earn rewards for various new mobile games in Playtime. To predict which mobile game will best suit a user, the team has introduced ML algorithms.

To build this rewarded experience, adjoe’s backend reliably handles more than 200 million users – remaining stable despite impressive growth in the user base. And imagine this: adjoe’s Golang event consumer applications assign rewards equivalent to 700 months of time spent in Playtime every day to its users. 

The software depends on two main components.
1) adjoe’s Android mobile SDK – written in a combination of Java and Kotlin – which can be bundled by developers in their apps. 
2) The team’s highly scalable Golang backend, which is hosted on AWS and is otherwise responsible for developing a modern React (TypeScript) dashboard that provides adjoe’s clients and Business team colleagues with fast-loading insights.
What You Will Do:
  • Manage the technical onboarding of our newest app partners and ensure a smooth implementation process. 
  • Become the link between various stakeholders, teams, and clients to provide clients with solutions to maximize their business goals. 
  • Assist the supply business team by providing them with data-driven insights into how they can reach their KPIs based on the analysis, aggregation, and visualization of adjoe’s data. 
  • Put on your detective hat and support publishers with issues they may face when integrating adjoe, ranging all the way from basic issues in their mobile app code to fine-tuning configurations.
  • Proactively identify integration issues that publishers may face and work with the product team to create solutions for these issues.
  • Contribute to maintaining easy-to-understand documentation of the integration process.
  • Be part of an international English-speaking team dedicated to scaling our adtech platform beyond our hundreds of millions of monthly active users.
  • Who You Are:
  • You have a skill for communicating complex technical information to business teams and partners. 
  • You are able to turn data into clear and actionable insights and document these in understandable spreadsheets or wiki pages to drive business growth. 
  • You thrive in a team environment and are an excellent cross-team communicator.
  • You have excellent analytical and communication skills and can use SQL to query databases.
  • Basic Knowledge of HTML and JavaScript & you know you don’t eat cookies in IT.
  • 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 or have experience building your own apps.
  • 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