在 React 中开始使用 Storybook

你有没有试过将你所有的 UI 组件放在 React 中的一个地方?

如果您是 React 世界的新手,那么您可能不会。

这是什么意思?

反应-美丽-dnd 例子。

您在示例中看到的内容称为故事。 用于创建故事的工具称为 Storybook。

现在,您已经了解我们将在本文中讨论的内容。 事不宜迟,让我们探索一下。

什么是故事书?

Storybook 是一个用户界面隔离的开发环境,为您的组件提供了一个游乐场。 我们可以在不运行主应用程序的情况下以不同的方式使用我们的组件。 我们可以使用设置在其端口中运行故事书。

它不仅限于 React。 我们可以将故事书与大多数前端框架一起使用,如 Vue、Angular、Mithril、Marko、Svelte 等。

您可以找到有关故事书的更多信息 这里.

什么是故事?

故事定义了组件的呈现状态。 如果我们采用通用组件,我们可以通过 props 以不同的方式使用它。 我们可以为每个州写一个故事。

假设我们有一个 Button 组件。

一个按钮可以存在不同的状态,如禁用、加载、主要、次要、小、大、中等。如果我们列出所有状态,那么在教程中前进将非常困难。 我想你明白了。 开始使用故事书时,您会得到更多。

您可以看到按钮在不同情况下的故事(大、中、小)。

在项目中设置 Storybook

我们将在 React 项目中设置一个故事书。

我们走吧。

  • 使用以下命令创建一个 React 项目。 您可以随意命名。
npx create-react-app storybook-demo
  • 现在,使用以下命令在您的项目中安装故事书。
npx sb init

我们已经完成了故事书的设置。

故事书为我们提供了一个单独的服务器。

如何开始呢?

故事书会自动在我们的脚本文件中添加一个命令。 您可以在脚本部分的 package.json 文件中查看它。 目前,运行以下命令启动故事书服务器。

npm run storybook

Storybook 将使用 package.json 文件的脚本部分中给定的端口启动一个新服务器。 它会在我们的默认浏览器中自动打开故事书(与反应服务器相同)。

默认情况下,您会在其中看到不同的故事。 如果您不想要或保留它们以供参考,您可以删除它们。 正如我们在上一节中讨论的那样,一个按钮可以有多个状态,您可以在故事书中看到它们(并未提及所有状态)。 我们将在本教程的最后一部分为按钮编写大量故事。

探索故事书的不同部分并熟悉不同的部分。 我们将在本教程中介绍其中的一些。

让我们写下我们的第一个故事。

测试故事书

我们已经看到了故事书的运行以及其中的一些示例。

  • 在 src 文件夹中创建一个名为 Button 的文件夹。
  • 创建名为 Button.jsx、Button.css 和 constants.js 的文件
  • 将以下代码段中的相应代码放入文件中。

按钮.jsx

import React, { Component } from "react";
import PropTypes from "prop-types";

import "./Button.css";

import { buttonTypes, buttonVariants, buttonSizes } from "./constants";

class Button extends Component {
    static defaultProps = {
        isDisabled: false,
        type: "filled",
        variant: "oval",
        size: "medium",
        backgroundColor: "#1ea7fd",
        textColor: "#ffffff",
    };

    static buttonTypes = buttonTypes;
    static buttonVariants = buttonVariants;
    static buttonSizes = buttonSizes;

    renderButton = () => {
        const {
            text,
            isDisabled,
            type,
            variant,
            size,
            backgroundColor,
            textColor,
            onClick,
        } = this.props;
        return (
            <button
                onClick={onClick}
                className={`default ${variant} ${size} ${
                    isDisabled ? "disabled" : ""
                }`}
                style={
                    type === buttonTypes.outline
                        ? {
                              border: `1px solid ${backgroundColor}`,
                              color: "#000000",
                              backgroundColor: "transparent",
                          }
                        : {
                              backgroundColor: `${backgroundColor}`,
                              border: `1px solid ${backgroundColor}`,
                              color: textColor,
                          }
                }
                disabled={isDisabled}
            >
                {text}
            </button>
        );
    };

    render() {
        return this.renderButton();
    }
}

Button.propTypes = {
    text: PropTypes.string,
    isDisabled: PropTypes.bool,
    type: PropTypes.oneOf([buttonTypes.outline, buttonTypes.filled]),
    variant: PropTypes.oneOf([buttonVariants.oval, buttonVariants.rectangular]),
    size: PropTypes.oneOf([
        buttonSizes.small,
        buttonSizes.medium,
        buttonSizes.large,
    ]),
    backgroundColor: PropTypes.string,
    textColor: PropTypes.string,
    onClick: PropTypes.func,
};

export { Button };

按钮.css

.default {
    border: none;
    cursor: pointer;
    background-color: transparent;
}

.default:focus {
    outline: none;
}

.disabled {
    opacity: 0.75; 
    cursor: not-allowed;
}
.small {
    font-size: 12px;
    padding: 4px 8px;
}

.medium {
    font-size: 14px;
    padding: 8px 12px;
}

.large {
    font-size: 16px;
    padding: 12px 16px;
}

.oval {
    border-radius: 4px;
}

.rectangular {
    border-radius: 0;
}

常量.js

export const buttonTypes = {
    outline: "outline",
    filled: "filled",
};

export const buttonVariants = {
    oval: "oval",
    rectangular: "rectangular",
};

export const buttonSizes = {
    small: "small",
    medium: "medium",
    large: "large",
};

那是什么密码?

  Mac 上的 7 个最佳 PDF 编辑器可提高工作效率

我们已经为 Button 编写了一个可以以不同方式使用的通用组件。 现在,我们有一个可以有不同状态的组件。

让我们按照以下步骤编写我们的第一个故事。

  • 创建一个名为 Button.stories.jsx 的文件
  • 将 React 和我们的 Button 组件导入到文件中。
  • 现在,为我们的组件故事定义一个标题或路径。 我们将使用以下代码定义它。
export default {
   title: ‘common/Button’,
}

上面的代码会将当前文件中的所有故事放在 common/Button/ 目录中。

  • 导出一个带有强制属性的按钮,如下所示。
export const defaultButton = () => (
    <Button text=”Default Button” onClick={() => {}} />
);

我们已经完成了我们的第一个故事。 使用以下命令运行故事书并查看输出。

npm run storybook

我们会写更多的故事,最后,别担心。

它在前端开发中有什么用?

使用故事书的主要优势是什么?

假设我们在一个 10 人的团队中工作。 我们需要检查每个人为当前工作项目编写的公共组件。

我们该怎么做?

我们必须去每个公共组件检查它们。 但是,这很耗时,不是我们的首选方式。 我们的新客座故事书来了。

如何利用它来克服我们的问题?

我们可以使用故事书为公共组件(任何 UI 组件)编写故事。 每当你的队友想要检查其他人的公共组件时,他们只需运行故事书服务器,就会看到所有的 UI 组件,就像我们上面看到的那样。

我们可以使用故事书中的渲染组件做更多的事情。 Storybook 有一个名为 Addons 的概念,它为我们的故事赋予超能力。

假设我们必须检查故事书中 UI 组件的响应能力,我们可以在故事书中使用一个名为 Viewport 的插件。 我们将在接下来的部分中了解有关插件的更多信息。

使用故事书

在本节中,我们将编写不同的故事来定义我们常用组件 Button 的不同状态。

写故事并没有那么困难。 故事定义了组件的状态。 如果您看到组件的 props,那么您将很容易理解该组件的不同用例。

让我们通过提供可选道具来写一些故事。

export const largeButton = () => (
    <Button text="Large Button" onClick={() => {}} size="large" />
);
export const outlineSmallButton = () => (
    <Button
        text="Outline Small Button"
        onClick={() => {}}
        size="small"
        type="outline"
    />
);
export const rectangularLargeButton = () => (
    <Button
        text="Rectangular Large Button"
        onClick={() => {}}
        size="large"
        variant="rectangular"
    />
);


export const disabledButton = () => (
    <Button text="Disabled Button" onClick={() => {}} isDisabled={true} />
);


export const warningButton = () => (
    <Button
        text="Warning Button"
        onClick={() => {}}
        backgroundColor="orange"
    />
);

以上三个故事定义了我们组件 Button 的不同用例。 现在,轮到你为我们的公共组件添加一些其他的故事案例了。 尝试添加 disabledSamllRectangularButton、dangerButton、successDisabledButton 等…,

我不会为上述情况提供代码。 你必须自己写才能理解它。 您可以看到我们到目前为止编写的完整故事代码。

import React from "react";

import { Button } from "./Button";

export default {
    title: "src/common/Button",
};

export const defaultButton = () => (
    <Button text="Default Button" onClick={() => {}} />
);

export const largeButton = () => (
    <Button text="Large Button" onClick={() => {}} size="large" />
);

export const outlineSmallButton = () => (
    <Button
        text="Outline Small Button"
        onClick={() => {}}
        size="small"
        type="outline"
    />
);

export const rectangularLargeButton = () => (
    <Button
        text="Rectangular Large Button"
        onClick={() => {}}
        size="large"
        variant="rectangular"
    />
);

export const disabledButton = () => (
    <Button text="Disabled Button" onClick={() => {}} isDisabled={true} />
);

export const warningButton = () => (
    <Button
        text="Disabled Button"
        onClick={() => {}}
        backgroundColor="orange"
    />
);

现在,您已经完全掌握了为组件编写故事的能力。

  8 个在线端口扫描器,用于查找服务器和 IP 上打开的端口

让我们跳到下一节,我们将了解插件以及它们如何提升我们的故事。

故事书插件

默认情况下,我们将提供多个插件。 在本节中,我们将探索对我们的开发最有用的插件。

让我们提升我们的 Button 故事。

控件

控件添加了一项功能,可以为故事书本身中的组件提供自定义道具。 对于我们的 Button 组件,我们可以添加控件来更改故事书中的不同道具。

假设我们必须找出按钮背景颜色的最佳颜色。 如果我们测试它通过一个一个地给组件来检查背景颜色,这将是非常耗时的。 相反,我们可以添加一个控件,允许我们在故事书中选择不同的颜色。 我们可以测试故事书本身的背景颜色。

让我们看看如何向我们的 Button 故事添加控件。

首先,我们必须如下定义标题下方的所有道具。

export default {
    title: "src/common/Button",
    argTypes: {
        text: { control: "text" },
        backgroundColor: { control: "color" },
        isDisabled: { control: "boolean" },
        size: {
            control: { type: "select", options: ["small", "medium", "large"] },
        },
        type: {
            control: { type: "select", options: ["filled", "outline"] },
        },
        variant: {
            control: { type: "select", options: ["oval", "rectangular"] },
        },
    },
};

接下来,将 props 从组件中分离出来,并将它们作为 args 提供,如下所示。

export const outlineSmallButton = (args) => (
    <Button {...args} onClick={() => {}} />
);
outlineSmallButton.args = {
    text: "Outline Small Button",
    size: "small",
    type: "outline",
};

您可以在组件预览窗口的底部看到控件。

您可以在组件预览窗口的底部看到控件选项卡。 玩它。

如上所述更新所有故事。 这更像是了解故事书插件的语法。 在 argTypes 中,我们使用了不同类型的控件。 您可以找到故事书中存在的所有控件 这里.

更新后的按钮故事将如下所示。

import React from "react";

import { Button } from "./Button";

export default {
    title: "src/common/Button",
    argTypes: {
        text: { control: "text" },
        backgroundColor: { control: "color" },
        isDisabled: { control: "boolean" },
        size: {
            control: { type: "select", options: ["small", "medium", "large"] },
        },
        type: {
            control: { type: "select", options: ["filled", "outline"] },
        },
        variant: {
            control: { type: "select", options: ["oval", "rectangular"] },
        },
    },
};

export const defaultButton = (args) => <Button {...args} onClick={() => {}} />;
defaultButton.args = {
    text: "Default Button",
};

export const largeButton = (args) => (
    <Button {...args} onClick={() => {}} size="large" />
);
largeButton.args = {
    text: "Large Button",
};

export const outlineSmallButton = (args) => (
    <Button {...args} onClick={() => {}} />
);
outlineSmallButton.args = {
    text: "Outline Small Button",
    size: "small",
    type: "outline",
};

export const rectangularLargeButton = (args) => (
    <Button {...args} onClick={() => {}} />
);
rectangularLargeButton.args = {
    text: "Rectangular Large Button",
    size: "large",
    variant: "rectangular",
};

export const disabledButton = (args) => <Button {...args} onClick={() => {}} />;
disabledButton.args = {
    text: "Disabled Button",
    isDisabled: true,
};

export const warningButton = (args) => <Button {...args} onClick={() => {}} />;
warningButton.args = {
    text: "Warning Button",
    backgroundColor: "orange",
};

动作

动作是 JavaScript 中的事件。 我们可以点击一个按钮,它是 JavaScript 中的一个事件。 我们可以使用 actions 插件对按钮单击执行一些操作。

  如何在 Adob​​e Photoshop 中解锁图层

通过操作,我们可以测试事件是否正常工作。 禁用的按钮不能被点击,启用的按钮必须是可点击的。 我们可以使用这些操作来确保它。

让我们看看如何为按钮点击添加动作。

我们之前已经为 onClick 道具提供了匿名功能。 现在,我们必须更新它。

  • 使用以下语句从故事书插件导入操作。
import { action } from "@storybook/addon-actions";
  • 将所有 () => {} 替换为以下语句。
action("Button is clicked!")

现在,转到故事书并单击一个按钮。 您将在控制选项卡旁边的操作选项卡下看到打印的消息。 如果您单击已禁用的按钮,则不会打印该消息,因为它已被禁用。

我们可以对 onChange、onMouseOver、onMouseOut 等不同事件使用该操作,以确保它们正常工作。 尝试为输入元素的 onChange 实现相同的方法。

有关操作,请参阅文档 这里.

背景

我们可以使用背景插件更改预览窗口的背景。 我们不必编写任何代码。 只需在故事书中更改它即可。 你可以看到下面的 gif。

视口

我们还可以测试故事书中组件的响应能力。 请参阅下面的 gif 以了解有关视口选项的信息。

文档

我们可以使用文档插件在故事书中记录我们的组件。 当我们在团队中工作时,它更有用。 他们将阅读组件并直接理解它。 它为开发人员节省了大量时间。

在故事书的组件预览窗口中,您可以在画布选项卡的右上角看到文档。 它将包含组件所有故事的所有文档。 如果我们想为包含 markdown 和组件渲染的组件编写文档,我们必须使用 Button.stories.mdx。 我们只是在其中编写一些额外的降价代码以及组件故事。

我们正在为我们的故事写一份文件。 代码包括 markdown 和组件渲染。 这只是学习语法。 你会第一眼看到它。

让我们看看 Button.stories.mdx 文档代码。

<!--- Button.stories.mdx -->

import {
    Meta,
    Story,
    Preview,
    ArgsTable
} from '@storybook/addon-docs/blocks';

import { Button } from './Button';

<Meta title="MDX/Button" component={Button} />

# Button Documentation

With `MDX` we can define a story for `Button` right in the middle of our
Markdown documentation.

<ArgsTable of={Button} />

export const Template = (args) => <Button {...args} />

## Default Button
We can write the documentation related to the Default Button
<Preview>
    <Story name="Default Button" args={{
        text: 'Default Button'
    }}>
    {Template.bind({})}
   </Story>
</Preview>

## Large Button
We are writing sample docs for two stories, you can write rest of them
<Preview>
    <Story name="Large Button" args={{
        text: "Large Button",
        }}>
        {Template.bind({})}
    </Story>
</Preview>

了解有关记录组件的更多信息 这里.

您可以找到有关附加组件的更多信息 这里.

结论

希望您喜欢本教程并了解故事书。 并在您的团队中有效地使用它来提高您的工作效率。

React 新手? 查看这些学习资源。

快乐编码🙂