javascript 故事书:如何为对象属性定义argType?

zzzyeukh  于 12个月前  发布在  Java
关注(0)|答案(2)|浏览(113)

我有一个输入Web组件,它有一个非常简单的API --它既有一个get 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"
  }
}

现在来描述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'] },
        },
    },
};

它在Storybook中呈现如下:

正如您所看到的,我没有为label方面获得适当的控制,例如。label.position的下拉列表。事实上,即使我根本没有定义argTypes.label,也会得到非常相同的结果。
当然,我可以在state结构上妥协,使所有label属性 * 扁平 * state属性,如labelTextlabelPositionlabelToolTip。但据我所知,Storybook并不打算以这种方式影响设计决策。
这似乎是一个非常基本的要求,我很惊讶我在文档中找不到任何关于它的东西。

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

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

编辑:

到目前为止,我试图解决当前的限制:
我使用一个TemplateFactory函数来替换奇数的Template.bind({}),只是为了创建一个新的示例。我们的每个组件都支持通过el.state setter设置组件状态。

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

export function TemplateFactory(tagName) {
    return (args) => {
        const el = document.createElement(tagName);
        el.state = ArgsParser.expand(args);
        return el;
    };
}
// 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;
    }
}
// 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"
}
*/

这导致:

如果我现在修改3个标签属性的控件,它不会影响组件。此外,处于初始状态的标签也消失了。
如果我指定扩展模型:

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

然后我得到这个

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

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

5fjcxozz

5fjcxozz1#

这是我在一个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 };
};

这将呈现一个带有文本字段的标记为arg1.label的额外控件。当我在这个字段中输入数据时,故事会被重新呈现,标签字段会被该文本替换。
我可以手动定制任何参数的属性,只需添加一个额外的argType,并将该参数的值传递回真实的的参数。

cnh2zyt3

cnh2zyt32#

显然argTypes不支持嵌套属性。但是您可以在render()函数中将自定义参数Map到组件的属性。
下面是一个react和typescript的例子:

type MyComponentAndCustomArgs = React.ComponentProps<typeof MyComponent> & {
  myCustomArg: string;
};

const meta = {
  title: "Example/MyComponent",
  component: MyComponent,
  tags: ["autodocs"],
  argTypes: {
    myCustomArg: { control: "text" },
  },
  render: ({ myCustomArg, ...args }) => {
    const props: MyComponentProps = { ...args };
    props.axis.color = myCustomArg;

    return <MyComponent {...props}></MyComponent>;
  },
} satisfies Meta<MyComponentAndCustomArgs>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
  args: {
    axis: {
      color: "black",
    },
    myCustomArg: "red",
  },
};

相关问题