返回Storybook 入门
章节
  • 开始
  • 简单组件
  • 复合组件
  • 数据
  • 总结
  • 贡献

构建简单组件

隔离构建简单组件
本社区翻译尚未更新至最新版 Storybook。请将英文指南中的更改应用于此翻译,帮助我们进行更新。 欢迎提交 Pull Request.

我们将遵循组件驱动开发 (CDD) 方法来构建 UI。这是一个从“自下而上”的过程,从组件开始,最终形成完整的页面。CDD 有助于你在构建 UI 时应对不断增加的复杂度。

任务

Task component in three states

Task 是我们应用中的核心组件。每个任务根据其确切状态会略有不同地显示。我们展示一个选中(或未选中)的复选框、一些关于任务的信息以及一个“固定”按钮,允许我们将任务在列表中上下移动。综合起来,我们需要这些 props

  • title – 描述任务的字符串
  • state - 任务当前在哪一个列表中,以及是否已完成?

当我们开始构建 Task 时,我们首先编写与上面草拟的不同类型的任务相对应的测试状态。然后我们使用 Storybook 隔离地构建组件,使用模拟数据。我们将逐步对组件在每种状态下的外观进行“可视化测试”。

这个过程类似于测试驱动开发(TDD),我们可以称之为“可视化 TDD”。

环境搭建

我们打开 .storybook/main.js 文件并看一下

复制
.storybook/main.js
module.exports = {
  stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
  addons: [
    '@storybook/addon-ondevice-controls',
    '@storybook/addon-ondevice-actions',
  ],
};

如果你查看 stories 属性,你会看到 Storybook 在 components 文件夹中查找 stories。

在 React Native 的 Storybook 中,由于 Metro bundler 的当前限制,我们依赖于 main.js 中的配置来生成一个名为 storybook.requires 的文件,该文件用于加载项目中的所有 stories 和插件。当你运行 yarn storybook 来启动 Storybook 时,该文件会自动生成。

💡 你也可以通过运行 yarn storybook-generate 命令手动生成 storybook.requires 文件。但是,除非你看到某个 story 没有加载,或者注意到 main.js 配置中的更改没有反映在你的 Storybook 中,否则你不需要重新创建这个文件。要了解更多关于如何生成 storybook.requires 文件的信息,你可以查看你的 metro.config.js 文件中的 withStorybook 函数。

现在我们来创建任务组件及其配套的 story 文件:components/Task.jsxcomponents/Task.stories.jsx

我们将从 Task 的基础实现开始,它只需接收我们所需的属性以及你可以对任务执行的两种操作(在列表之间移动任务)

复制
components/Task.jsx
import { StyleSheet, TextInput, View } from 'react-native';
import { styles } from './styles';

export const Task = ({
  task: { id, title, state },
  onArchiveTask,
  onPinTask,
}) => {
  return (
    <View style={styles.listItem}>
      <TextInput value={title} editable={false} />
    </View>
  );
};

现在添加 story 文件

复制
components/Task.stories.jsx
import { Task } from './Task';

export default {
  title: 'Task',
  component: Task,
  argTypes: {
    onPinTask: { action: 'onPinTask' },
    onArchiveTask: { action: 'onArchiveTask' },
  },
};

export const Default = {
  args: {
    task: {
      id: '1',
      title: 'Test Task',
      state: 'TASK_INBOX',
    },
  },
};

export const Pinned = {
  args: { task: { ...Default.args.task, state: 'TASK_PINNED' } },
};

export const Archived = {
  args: { task: { ...Default.args.task, state: 'TASK_ARCHIVED' } },
};

在我们的 stories 文件中,我们使用了名为 Component Story Format (CSF) 的语法。这种语法使得编写 stories 更加容易,并且受到最新版 Storybook 的支持。

Storybook 中有两种基本的组织层级:组件及其子 stories。可以将每个 story 视为组件的一种变体。一个组件可以拥有任意数量的 stories。

  • 组件
    • Story
    • Story
    • Story

为了让 Storybook 了解我们正在文档化的组件,我们创建一个 default 导出,其中包含

  • component -- 组件本身
  • title -- 在 Storybook 应用的侧边栏中如何引用该组件
  • argTypes -- 允许我们指定 args 的类型,这里我们用它来定义动作,这些动作将在每次交互发生时被记录下来

为了定义我们的 stories,我们导出一个带有 args 属性的对象。参数(简称 args)允许我们使用 controls 插件实时编辑组件,而无需重启 Storybook。一旦 args 的值发生变化,组件也会随之变化。

创建 story 时,我们使用一个基础的 task arg 来构建组件期望的任务结构。通常根据实际数据建模。同样, export-ing 导出这种结构将使我们能够在后续的 stories 中重用它,稍后你将看到。

现在我们已经完成了基础设置,让我们重新运行 yarn storybook 命令,看看变化。如果你已经有 Storybook 在运行,你也可以运行 yarn storybook-generate 来重新生成 storybook.requires 文件。

你应该会看到一个类似这样的 UI: 一个展示 Storybook 中任务组件的 gif

你可以使用屏幕底部的菜单打开导航菜单,然后点击在 stories 之间切换。之后,你可以点击其他地方或向下拖动来关闭菜单。通过按下底部栏最右侧的图标,你可以找到插件。

构建状态

现在我们可以开始构建组件以匹配设计稿。

目前组件仍然很基础。首先编写实现设计稿的代码,无需过于深入细节。

复制
components/Task.jsx
import { MaterialIcons } from '@expo/vector-icons';
import { StyleSheet, TextInput, TouchableOpacity, View } from 'react-native';
import { styles } from './styles';

export const Task = ({
  task: { id, title, state },
  onArchiveTask,
  onPinTask,
}) => {
  return (
    <View style={styles.listItem}>
      <TouchableOpacity onPress={() => onArchiveTask(id)}>
        {state !== "TASK_ARCHIVED" ? (
          <MaterialIcons
            name="check-box-outline-blank"
            size={24}
            color="#26c6da"
          />
        ) : (
          <MaterialIcons name="check-box" size={24} color="#26c6da" />
        )}
      </TouchableOpacity>
      <TextInput
        placeholder="Input Title"
        value={title}
        editable={false}
        style={
          state === "TASK_ARCHIVED"
            ? styles.listItemInputTaskArchived
            : styles.listItemInputTask
        }
      />
      <TouchableOpacity onPress={() => onPinTask(id)}>
        <MaterialIcons
          name="star"
          size={24}
          color={state !== "TASK_PINNED" ? "#eee" : "#26c6da"}
        />
      </TouchableOpacity>
    </View>
  );
};

完成后,它应该看起来像这样

a gif showing the task component in storybook

组件构建完成!

现在我们已经成功地构建了一个组件,而无需服务器或运行整个应用程序。下一步是以类似的方式逐个构建剩余的 Taskbox 组件。

如你所见,隔离构建组件既简单又快速。我们可以期望产出更高质量、更少 bug、更精良的 UI,因为可以深入测试每一种可能的状态。

💡 别忘了用 git 提交你的更改!
这篇免费指南对你有帮助吗?发推文赞扬并帮助其他开发者找到它。
下一章
复合组件
从简单组件组装复合组件
✍️ 在 GitHub 上编辑 – 欢迎 PR!
加入社区
6,975开发者正在使用
为何使用为何选择 Storybook组件驱动 UI
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI