Storybook is currently the most popular UI component development environment. The main function of Storybook is to provide an independent runtime environment for components to be written and used by components. Each sample is called a “story”. With it, you can easily extract common components from a project and manage your own component library.

Moreover, it can also achieve interactive testing, responsive layout, document generation, design preview and other functions through rich plug-ins, making development work more focused and collaboration with designers easier.

Install the Storybook

Storybook has a wealth of custom configuration items and a large number of plugins that can be adapted to a variety of development scenarios, but it can also make building projects complicated and difficult for newcomers to quit. 🤣

Fortunately, with the recent update of version 6.0, projects can be built with zero configuration, just like Spring Boot simplified to Spring, which makes project creation very easy. Now projects can be built with a single command.

Storybook supports various major component frameworks, illustrated here with Angular.

Creating an Angular project

Angular CLI is installed globally by default: NPM install -g@angular/CLI. Angular 11, by the way, dramatically increases build speed and package size without disruptive changes, and is a powerful benefit. 😋

Create an Angular project by running ng new Storybook-Example in the specified directory (such as D:\projects) and follow the prompts to generate an interactive configuration project.

Initialize the Storybook

To complete the storybook setup, run NPX sb init in the project path (D: projects\storybook-example). It automatically configures the Storybook based on the project’s dependencies. With just one line of command, the Storybook is automatically installed into the project.

After installation, runnpm run storybookSo easy! 😎

What does initialization do?

Although the project is up and running, suddenly a bunch of unknown files are automatically created, which is also unnerving. Let’s take a look at what the project initialization does.

  • 📦 Install required dependencies: The Angular version of Storybook dependencies is installed because it is identified as an Angular project in the current directory.
    • “@compodoc/compodoc”: Angular component documentation tool.
    • “@storybook/ Addon-Actions “: plug-in for recording event triggers.
    • “@storybook/ Addon-Essentials “: officially maintained collection of plug-ins with default configuration.
    • “@storybook/addon-links”: used to create links to component stories.
    • “@storybook/angular”: Storybook for Angular dependencies.
  • 🛠 Set NPM script:
    • “Storybook “: Runs storybook locally
    • “Build-storybook “: Compile and package the storybook project
  • 🛠 is created in the project root directory.storybookFolder, add default configuration:
    • Main.js: The global configuration file for the project, which defines the path to find the story, and the imported plug-ins.
    • Preview.js: Render configuration for the project, including the introduction of global styles, generic variables, etc.
    • Tsconfig. json: Automatically identifies configuration files automatically generated after typescript is adopted
  • 📝 insrc/storiesCreate three components (Button, header, page) and their story samples under the directory

Write stories

Stories are used to show the state of a component, and each component can contain any number of stories that are used to test various scenarios for the component. By default, you only need to create it in the component folder as **.component.stories.ts.

Story grammar

The basic syntax is very simple: export any number of functions, each of which is a story. There are two main types of export:

  1. default exportThe default export provides component-level configuration information, such as the following configuration, which specifies the categorization of components and provides Button component information for rendering the component.
    // Button.stories.ts
    
    import Button from './button.component';
    
    export default {
      title: 'Components/Button'.component: Button,
    }
    Copy the code
  2. named export: named export to describe stories. As mentioned above, a component can have several stories.
    // Button.stories.ts
    
    // Create a template that can be reused in subsequent stories
    const Template = (args: Button) = > ({
      props: args,
    });
    
    export const Primary = Template.bind({});  / / copy the Template
    Primary.args = { background: '#ff0'.label: 'Button' };
    Primary.storyName = "Primary state" // Create a custom story name
    
    export constSecondary = Template.bind({}); Secondary.args = { ... Primary.args,label: '😄 👍 😍 💯' }; // Reuse the configuration of the previous story
    
    export constTertiary = Template.bind({}); Tertiary.args = { ... Primary.args,label: '📚 📕 📈 🤓' } // One more
    Copy the code

    By copying the template function, you can create several stories and pass in different parameters to render different states of the component. The name of each story defaults to the function name and can be customized.

Args (attributes)

Last time we saw how to write a Story file, but what are the args that are used over and over again? It represents the component’s input properties (@input() in Angular, props in Vue/React), and has two levels for flexible configuration.

  1. Story level:

    // Button.stories.ts
    
    const Template = (args: Button) = > ({
      props: args,
    });
    
    // Pass component properties in this story that affect only the current story
    export const Primary = Template.bind({});
    Primary.args = {
      primary: true.label: 'Primary'};Copy the code
  2. Component level:

    // Button.stories.ts
    
    import Button from './button.component';
    
    // Component properties are passed in at the component level (default export),
    // The primary attribute for all stories of the Button component will be true
    export default {
      title: "Button".component: Button,
      args: {
        primary: true,}}Copy the code

As we saw in the previous section, args for different stories can be reused, using ES6 deconstruction syntax:

const Primary = ButtonStory.bind({});
Primary.args = {
  primary: true.label: 'Button',}// Reuse the Primary story's args and override the Primary attribute
constSecondary = ButtonStory.bind({}); Secondary.args = { ... Primary.args,// Merge the last args object
  primary: false,}Copy the code

Simply export a few functions, and you’ll have a test case for the button component

As you can see, the button component can now run independently of the project, and the right-hand toolbar is free to change its properties, see the effects of these changes in real time, and automatically generate component documentation.

There’s a story for example, a live console, documentation, and no fear of writing components that people won’t know how to use. 😎

Additional configuration items

In addition to writing a story to a component, many times you will need to configure a plug-in or provide additional functionality to the component. Here’s how to configure it.

Parameters:

Parameters are used to configure Storybook and plugins at the global, component, and Story levels.

Story has a wide variety of wallpapers. We use simple wallpapers to control the background color of our components.

  1. The global definition is in the root directory.storybook/preview.js and affects all stories. After this configuration, each story screen can choose red/green background:

    // .storybook/preview.js
    
    export const parameters = {
      backgrounds: {
        values: [{name: 'red'.value: '#f00' },
          { name: 'green'.value: '#0f0'},],}};Copy the code
  2. The component-level definition allows all stories of the component to select the specified background color

    // Button.story.ts
    
    export default {
      title: 'Button'.component: Button,
      parameters: {
        backgrounds: {
          values: [{name: 'red'.value: '#f00' },
            { name: 'green'.value: '#0f0'},],},},},};Copy the code
  3. The definition at the story level only affects the current story, leaving all other stories with the default black/white color.

    // Button.story.ts
    
    export const Primary = Template.bind({});
    Primary.args = {
      primary: true.label: 'Button'};// Red/green background can only be selected under this story
    Primary.parameters = {
      backgrounds: {
        values: [{name: 'red'.value: '#f00' },
          { name: 'green'.value: '#0f0'},],}};Copy the code

The configuration of Parameters is inheritable, and a child of the same name overrides the parent’s definition.

Decorators

Each Decorator is also a function that wraps the story, adding extra DOM elements, introducing context, adding fake data, and so on, without leaving the original story intact. Like Parameters, it can be defined at the global/component/Story level, and each Decorator executes in defined order, from global to story.

For example, wrap each story’s component render with an extra

:
// button.stories.ts

import { Meta, Story } from '@storybook/angular';
import { ListComponent } from './list.component';

export default {
  title: 'Example/List'.component: ListComponent,
  decorators: [
    (storyFunc) = > {
      const story = storyFunc();

      return {
        ...story,
        template: `<div style="height: 60px">${story.template}</div>`}; }}]as Meta;
Copy the code

This way, all the stories in the list component will show how they would look in a 60-pixel container.

In addition to wrapping components with additional elements, such as adding component dependencies to composite components:

// List.stories.ts

import { moduleMetadata } from '@storybook/angular';
import { CommonModule } from '@angular/common';

import List from './list.component';
import ListItem from './list-item.component'


// Add the required components and module dependencies to the list component
export default {
  title: 'List'.component: List,
  decorators: [
    moduleMetadata({
      declarations: [ListItem],
      imports: [CommonModule],
    }),
  ],
};

const Template = (args: List) = > ({
  component: List,
  props: args,
});
Copy the code

Just as you would normally declare in an ngModule, the moduleMetadata decorator makes it easy to test various components, allowing you to build component libraries from small to large in your Storybook.

Component-driven development

Modern user interface is more and more complex, in the rapid development of technology, people increasingly expect unique, personalized user experience. But it also means that the front end and the design need to embed more logic and design into the UI interface, which makes the front end heavier and the interface more complex to maintain and test. It is also a trend to separate UI modules, separating page logic into separate interactive modules, deconstructing complex businesses, and making each module easy to test, combine, and reuse. So now componentized frameworks are absolutely mainstream. Similarly, microservices and containerization are becoming very popular these days.

The development process

What about component-driven development?

  1. Write components one ata time: Write individual components first and define their Input properties and output events, starting ata micro level (for example, Avatar, Button, Input, Tooltip). Yes, that’s what the familiar component libraries do: Material, Antd, ElementUI…
  2. Composition components: Make up more complex business modules that make up new functions (such as Forms, headers, lists, tables).
  3. Assemble pages: Build business modules into pages, using dummy data to simulate various usage scenarios and edge use cases for the pages. (for example, home page, setting page, personal home page)
  4. Integrate page into application: connect the page with background data interface and business logic service layer to form actual application programs (such as XX supervision system, XXX mall and XX official website)

So after Storybook initializes the project, it creates three component examples, Button, Header, and Page, that represent such a development ladder.

Applicable scenario

There is no one-size-fits-all development model, there is always a need to choose the technology stack and development model on demand, and it is important to understand the pros and cons of each scenario, and this is no exception.

Advantage;

  • High quality: Write stories to verify the running scenarios of components. The more test cases, the more reliable and robust the quality of components.
  • Easy to test: Test at the component level and refine the debug to be less effort and more accurate than testing by business page.
  • Parallel development and design: UI modular development by component allows design and front-end development to work more closely together, with different projects sharing resources.

Disadvantage:

  • Time cycle: Design API rigorously for components, write stories, consider reusability and edge scenarios, rather than developing pages in a shuttle. The development cycle of the project will inevitably increase, which is not friendly to the outsourcing partner.
  • Page application: The whole application is a collection of several complete pages from the perspective of development and design. There are not many reusable elements between pages. There is little difference between overnight development and component division (developing various large visual screens may have the same experience).

Therefore, component-driven development is more suitable for a series of application scenarios with rich reuse scenarios, sufficient project life cycle, high quality requirements, and close front-end and design cooperation teams (or, our own personal project of a well-rounded 😁).

conclusion

This article gives a brief introduction to the Storybook component development tool, including the following points:

  • In an existing Angular project, the Storybook development environment is initialized with a single command, which automatically installs dependencies, creates default configurations, and generates three examples in the project. Run the command line it sets up to see the component test screen.
  • Describes how to write a component story.
    • Write three story use cases of key components to realize the reuse of use cases.
    • Configure the background plug-in with parameter.
    • Use the decorator to add an extra outer DOM to the component, passing the component’s module dependencies.
  • This paper introduces the component-driven development mode and analyzes its advantages and disadvantages.

It’s easy to see on paper, but we need to integrate it into our actual development scenarios and see how it drives the development of projects. So, let’s experience visual test-driven development and complete a full business page. See you next time! 😎