Storybook:如何为对象属性定义 argType?

con*_*exo 21 javascript storybook

我有一个输入 Web 组件,它有一个非常简单的 API - 它有 aget state()set state(model). 该 Web 组件还处理label用于输入的 ,因此简单的model如下所示:

{
  "value": "Foobar",
  "required": true,
  "disabled": false,
  "label": {
    "text": "Label for input",
    "toolTip": "Tooltip for the input's label",
    "position": "east"
  }
}
Run Code Online (Sandbox Code Playgroud)

现在为了描述argTypes故事,我尝试了以下方法:

export default {
    title: `Components/Input/text-input`,
    argTypes: {
        value: { control: 'text' },
        disabled: { control: 'boolean' },
        required: { control: 'boolean' },
        label: {
            text: { control: 'text' },
            toolTip: { control: 'text' },
            position: { control: 'select', options: ['north', 'south', 'east', 'west'] },
        },
    },
};
Run Code Online (Sandbox Code Playgroud)

它在 Storybook 中的呈现如下:

在此输入图像描述

正如您所看到的,我没有对该label方面进行适当的控制,例如 的下拉菜单label.position。事实上,即使我argTypes.label根本不定义,我也会得到完全相同的结果。

当然,我可以在我的state结构上做出妥协,让所有label属性都成为扁平 state属性,例如labelTextlabelPosition、 和labelToolTip。但据我了解,Storybook 并不意味着以这种方式影响设计决策。

这似乎是一个非常基本的要求,我很惊讶我在文档中找不到任何相关内容。

问题:那么如何在不改变模型结构的情况下实现这一目标呢?

注意:我使用的是 Storybook HTML v6.3.8。

编辑:

到目前为止,我已尝试解决当前的限制:

我使用一个TemplateFactory函数来替换奇数Template.bind({})只是为了创建一个新实例。我们的每个组件都支持通过 setter 设置组件状态el.state

import { ArgsParser } from '../helper/ArgsParser.js';

export function TemplateFactory(tagName) {
    return (args) => {
        const el = document.createElement(tagName);
        el.state = ArgsParser.expand(args);
        return el;
    };
}
Run Code Online (Sandbox Code Playgroud)
// ArgsParser
export class ArgsParser {
    static flat = (args) => {
        const parsedArgs = {};
        for (const [key, value] of Object.entries(args)) {
            if (['string', 'boolean', 'number'].includes(typeof value)) {
                parsedArgs[key] = value;
            } else {
                for (const innerKey in value) parsedArgs[`${key}.${innerKey}`] = value[innerKey];
            }
        }
        return parsedArgs;
    };

    static expand(args) {
        const parsedArgs = {};
        for (const [key, value] of Object.entries(args)) {
            const parsedKeys = key.split('.');
            if (parsedKeys.length === 1) {
                parsedArgs[key] = value;
            } else {
                const [parentKey, prop] = parsedKeys;
                parsedArgs[parentKey] = parsedArgs[parentKey] ?? {};
                parsedArgs[parentKey][prop] = value[prop];
            }
        }
        return parsedArgs;
    }
}
Run Code Online (Sandbox Code Playgroud)
// custom-text.stories.js
import { TemplateFactory } from '../helper/TemplateFactory.js';
const TAG_NAME = 'custom-text';

export default {
    title: `Components/Input/${TAG_NAME}`,
    argTypes: {
        value: { control: 'text' },
        disabled: { control: 'boolean' },
        required: { control: 'boolean' },
        ['label.text']: { control: 'text' },
        ['label.toolTip']: { control: 'text' },
        ['label.position']: {
            control: { type: 'select', options: ['north', 'south', 'east', 'west'] },
        },
    },
};

export const EmptyEnabled = TemplateFactory(TAG_NAME);
EmptyEnabled.args = ArgsParser.flat({
    value: '',
    disabled: false,
    label: {
        text: 'Empty and Labeled',
        toolTip: 'A beautiful tooltip',
        position: 'north',
    },
});
/* assigns
{
  "value": "",
  "disabled": false,
  "label.text": "Empty and Labeled",
  "label.toolTip": "A beautiful tooltip",
  "label.position": "north"
}
*/
Run Code Online (Sandbox Code Playgroud)

这导致:

在此输入图像描述

如果我现在修改 3 个标签属性的控件,则不会影响组件。而且,初始状态下的标签也消失了。

如果我分配扩展模型:

export const EmptyEnabled = TemplateFactory(TAG_NAME);
EmptyEnabled.args = {
    value: '',
    disabled: false,
    label: {
        text: 'Empty and Labeled',
        position: 'north',
    },
};
Run Code Online (Sandbox Code Playgroud)

然后我得到这个:

在此输入图像描述

但是当我尝试使用单选按钮时,label.position它不会影响组件,但是(仅在选择两次之后)它会导致 JSON 突然显示undefinedposition

在此输入图像描述

label.text如果我编辑和/或,也会发生同样的情况label.toolTip

在此输入图像描述

ell*_*gan 4

这是我在 Angular 项目中的方法:

type InputPropOverrides = {
  'arg1.label': string,
};

export default {
  title: 'Components/MyComponent',
  component: MyComponent,
  decorators: [
    moduleMetadata({
      imports: [MyModule],
    }),
  ],
  argTypes: {
    arg1: { ... },
    'arg1.label': {
      control: {
        type: 'text',
      },
    },
  },
} as Meta;

const Template: Story<MyComponent & InputPropOverrides> = (args: MyComponent & InputPropOverrides) => {
  const updatedArgs = args;
  updatedArgs.menuItem.label = args['arg1.label'];
  return { props: updatedArgs };
};
Run Code Online (Sandbox Code Playgroud)

这将呈现一个标有文本字段的额外控件arg1.label。当我将数据输入该字段时,故事将重新呈现,标签字段将被该文本替换。

我可以通过添加额外的 argType 并将该 arg 的值传递回真实的 arg 来手动自定义任何参数的属性。