加入在线会议:美国东部时间周四上午 11 点,Storybook 9 发布及 AMA

构建时故事标题生成

一个 Babel 插件,用于在编译时为 Storybook CSF 故事生成标题,通常基于故事文件的文件名。

在 Github 上查看

babel-plugin-storybook-csf-title

Atlassian license PRs Welcome

一个用于在编译时Storybook CSF 故事生成标题的 Babel 插件,通常基于故事文件的文件名。

用法

该插件会基于作为插件选项提供的 toTitle 函数的结果,向所有转换后的文件添加一个 title 属性。

假设 toTitle: () => 'foo',有三种一般情况

1️⃣ 文件未提供默认导出

在这种情况下,插件会创建一个默认导出 { title: "foo" }

例如,

import React from 'react';
import Component from './index';

export const Example = () => <Component />;

转换为

import React from 'react';
import Component from './index';

export const Example = () => <Component />;

export default { title: 'foo' };

2️⃣ 文件提供一个对象作为其默认导出

在这种情况下,插件会将 title: foo 属性添加到现有导出中。

例如,

import React from 'react';
import Component from './index';

export default { 
    something: 'something'
};

export const Example = () => <Component />;

转换为

import React from 'react';
import Component from './index';

export default { 
    title: 'foo'
    something: 'something'
};

export const Example = () => <Component />;

如果现有导出已包含 title 属性,则会抛出错误。

3️⃣ 文件提供非对象作为其默认导出

如果设置了 renameDefaultExportsTo 选项,插件会假定默认导出是一个组件,并将其移动到名为 ${renameDefaultExportsTo} 的命名导出。然后它创建一个默认导出 { title: "foo" }

例如,假设 renameDefaultExportsTo"Default"

import React from 'react';
import Component from './index';

export default () => <Component />;

转换为

import React from 'react';
import Component from './index';

export const Default = () => <Component />;

export default { 
    title: 'foo'
};

如果名为 ${renameDefaultExportsTo} 的导出已存在,则会抛出错误。

安装

安装插件,例如通过 yarn

yarn add --dev babel-plugin-storybook-csf-title

在您的 Babel 配置中,将 babel-plugin-storybook-csf-title 添加为插件

plugins: [
    ['babel-plugin-storybook-csf-title', { toTitle: require('./your-to-title-function') }],
]

请注意,该插件仅适用于故事文件。您需要确保它仅应用于这些特定文件,例如这样

/* add plugin to babel, however disable it by default */
plugins: [
    ['babel-plugin-storybook-csf-title', false], 
],
/* enable the plugin for all files that match your story name pattern */
overrides: [{ 
    include: /\/stories\.(ts|tsx)$/, 
    plugins: [
        ['babel-plugin-storybook-csf-title', { toTitle: require('./your-to-title-function') }]
    ]
}]

选项

该插件接受三个选项:toTitle(必填)、ifTitleFound(可选)和 renameDefaultExportsTo(可选)

  • toTitle 是一个函数,对于每个转换的故事文件,它接收 Babel 的 state 对象,并且必须返回故事文件的标题作为字符串。大多数 toTitle 实现将根据 state.filename 做出决策。

  • ifTitleFound 是一个可选的字符串值,可以设置为

    • 'skip' - 如果代码中已手动指定标题,则跳过添加标题
    • undefined(或任何其他值)- 如果处理的文件已定义标题,则抛出错误
  • renameDefaultExportsTo 是一个可选的字符串值,用于控制上述场景 3。默认为 undefined

生成有意义的故事名称

在大多数情况下,故事名称将根据故事文件的文件名生成。以下是针对 yarn workspaces 风格的 monorepo 设置的 toTitle 的可能实现

const path = require('path');
const pkgUp = require('pkg-up');

module.exports = (state) => {

    // find the closest package.json
    const packageJsonPath = pkgUp.sync({ cwd: state.filename });

    // read the package.json
    const packageJson = require(packageJsonPath);

    // get the path of the story file relative to the package root
    const { dir: packageJsonDir } = path.parse(packageJsonPath);
    const { dir: fileDir, name: fileName } = path.parse(path.relative(packageJsonDir, state.filename));

    const storybookPath = [
        // package name; "/" has meaning to storybook, hence replace a possible "/" by "|"
        packageJson.name.replace('/', '|'),

        // file dir
        ...fileDir.split(path.sep),
    ];

    // handle file names
    if (fileName === 'examples' || fileName === 'stories') {
        // nothing to do
    } else if (fileName.endsWith('.stories')) {
        storybookPath.push(fileName.slice(0, '.stories'.length + 1));
    } else if (fileName.endsWith('.examples')) {
        storybookPath.push(fileName.slice(0, '.examples'.length + 1));
    }

    return storybookPath.join('/');
}

贡献

欢迎对 babel-plugin-storybook-csf-title 做出贡献!详情请参阅 CONTRIBUTING.md

许可

版权所有 (c) 2020 Atlassian 和其他人。根据 Apache 2.0 许可,详情请参阅 LICENSE 文件。

With ❤️ from Atlassian

  • obweger
    obweger
适用于
    React
标签