我有一个输入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
属性,如labelText
、labelPosition
和labelToolTip
。但据我所知,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突然显示position
的undefined
:
如果我编辑label.text
和/或label.toolTip
,也会发生同样的情况:
2条答案
按热度按时间5fjcxozz1#
这是我在一个Angular项目中的方法:
这将呈现一个带有文本字段的标记为
arg1.label
的额外控件。当我在这个字段中输入数据时,故事会被重新呈现,标签字段会被该文本替换。我可以手动定制任何参数的属性,只需添加一个额外的argType,并将该参数的值传递回真实的的参数。
cnh2zyt32#
显然
argTypes
不支持嵌套属性。但是您可以在render()
函数中将自定义参数Map到组件的属性。下面是一个react和typescript的例子: