Storybook 正在显示代码中的所有内容
Storybook is displaying everything in Show Code
我正在使用 Vue 3 + Storybook。一切正常,除了当我单击“显示代码”时,它只显示所有内容而不仅仅是模板。我做错了什么?
这是我的故事:
import Button from './Button.vue';
export default {
title: 'Components/Button',
component: Button
};
const Template = (args) => ({
// Components used in your story `template` are defined in the `components` object
components: { Button },
// The story's `args` need to be mapped into the template through the `setup()` method
setup() {
return { args };
},
// And then the `args` are bound to your component with `v-bind="args"`
template: '<my-button v-bind="args" />',
});
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: 'Button',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Button',
};
export const Large = Template.bind({});
Large.args = {
size: 'large',
label: 'Button',
};
export const Small = Template.bind({});
Small.args = {
size: 'small',
label: 'Button',
};
如下面的屏幕截图所示,它确实有效,正如您在 Vue 2 中所期望的那样。
但是,我使用 Vue 3 得到的结果与您相同。
简单的答案
它还没有为 Vue 3 实现。
正如您在 Storybook 文档插件的 source code 中看到的那样,
Vue 3 框架有一个单独的实现。但是,Vue 3 实现缺少源装饰器,它生成源代码的呈现版本。
修补程序
如果您不想等到 Storybook 团队发布更新,您可以根据您的论点使用以下代码生成您自己的文档。请记住,这并未涵盖所有用例。
const stringifyArguments = (key, value) => {
switch (typeof value) {
case 'string':
return `${key}="${value}"`;
case 'boolean':
return value ? key : '';
default:
return `:${key}="${value}"`;
}
};
const generateSource = (templateSource, args) => {
const stringifiedArguments = Object.keys(args)
.map((key) => stringifyArguments(key, args[key]))
.join(' ');
return templateSource.replace('v-bind="args"', stringifiedArguments);
};
const template = '<my-button v-bind="args" />';
const Template = (args) => ({
components: { MyButton },
setup() {
return { args };
},
template,
});
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: 'Button',
};
Primary.parameters = {
docs: {
source: { code: generateSource(template, Primary.args) },
},
};
另一个临时解决方案是手动编写源代码,而不是自动生成。
Primary.parameters = {
docs: {
source: { code: '<my-button primary label="Button" />' },
},
};
一种可能的选择是使用我在上述 link 的 GH 问题中找到的当前解决方法。
在 .storybook
文件夹中创建文件 withSource.js
,内容如下:
import { addons, makeDecorator } from "@storybook/addons";
import kebabCase from "lodash.kebabcase"
import { h, onMounted } from "vue";
// this value doesn't seem to be exported by addons-docs
export const SNIPPET_RENDERED = `storybook/docs/snippet-rendered`;
function templateSourceCode (
templateSource,
args,
argTypes,
replacing = 'v-bind="args"',
) {
const componentArgs = {}
for (const [k, t] of Object.entries(argTypes)) {
const val = args[k]
if (typeof val !== 'undefined' && t.table && t.table.category === 'props' && val !== t.defaultValue) {
componentArgs[k] = val
}
}
const propToSource = (key, val) => {
const type = typeof val
switch (type) {
case "boolean":
return val ? key : ""
case "string":
return `${key}="${val}"`
default:
return `:${key}="${val}"`
}
}
return templateSource.replace(
replacing,
Object.keys(componentArgs)
.map((key) => " " + propToSource(kebabCase(key), args[key]))
.join(""),
)
}
export const withSource = makeDecorator({
name: "withSource",
wrapper: (storyFn, context) => {
const story = storyFn(context);
// this returns a new component that computes the source code when mounted
// and emits an events that is handled by addons-docs
// this approach is based on the vue (2) implementation
// see https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/vue/sourceDecorator.ts
return {
components: {
Story: story,
},
setup() {
onMounted(() => {
try {
// get the story source
const src = context.originalStoryFn().template;
// generate the source code based on the current args
const code = templateSourceCode(
src,
context.args,
context.argTypes
);
const channel = addons.getChannel();
const emitFormattedTemplate = async () => {
const prettier = await import("prettier/standalone");
const prettierHtml = await import("prettier/parser-html");
// emits an event when the transformation is completed
channel.emit(
SNIPPET_RENDERED,
(context || {}).id,
prettier.format(`<template>${code}</template>`, {
parser: "vue",
plugins: [prettierHtml],
htmlWhitespaceSensitivity: "ignore",
})
);
};
setTimeout(emitFormattedTemplate, 0);
} catch (e) {
console.warn("Failed to render code", e);
}
});
return () => h(story);
},
};
},
});
然后将这个装饰器添加到preview.js
:
import { withSource } from './withSource'
...
export const decorators = [
withSource
]
我正在使用 Vue 3 + Storybook。一切正常,除了当我单击“显示代码”时,它只显示所有内容而不仅仅是模板。我做错了什么?
这是我的故事:
import Button from './Button.vue';
export default {
title: 'Components/Button',
component: Button
};
const Template = (args) => ({
// Components used in your story `template` are defined in the `components` object
components: { Button },
// The story's `args` need to be mapped into the template through the `setup()` method
setup() {
return { args };
},
// And then the `args` are bound to your component with `v-bind="args"`
template: '<my-button v-bind="args" />',
});
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: 'Button',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Button',
};
export const Large = Template.bind({});
Large.args = {
size: 'large',
label: 'Button',
};
export const Small = Template.bind({});
Small.args = {
size: 'small',
label: 'Button',
};
如下面的屏幕截图所示,它确实有效,正如您在 Vue 2 中所期望的那样。
但是,我使用 Vue 3 得到的结果与您相同。
简单的答案
它还没有为 Vue 3 实现。
正如您在 Storybook 文档插件的 source code 中看到的那样, Vue 3 框架有一个单独的实现。但是,Vue 3 实现缺少源装饰器,它生成源代码的呈现版本。
修补程序
如果您不想等到 Storybook 团队发布更新,您可以根据您的论点使用以下代码生成您自己的文档。请记住,这并未涵盖所有用例。
const stringifyArguments = (key, value) => {
switch (typeof value) {
case 'string':
return `${key}="${value}"`;
case 'boolean':
return value ? key : '';
default:
return `:${key}="${value}"`;
}
};
const generateSource = (templateSource, args) => {
const stringifiedArguments = Object.keys(args)
.map((key) => stringifyArguments(key, args[key]))
.join(' ');
return templateSource.replace('v-bind="args"', stringifiedArguments);
};
const template = '<my-button v-bind="args" />';
const Template = (args) => ({
components: { MyButton },
setup() {
return { args };
},
template,
});
export const Primary = Template.bind({});
Primary.args = {
primary: true,
label: 'Button',
};
Primary.parameters = {
docs: {
source: { code: generateSource(template, Primary.args) },
},
};
另一个临时解决方案是手动编写源代码,而不是自动生成。
Primary.parameters = {
docs: {
source: { code: '<my-button primary label="Button" />' },
},
};
一种可能的选择是使用我在上述 link 的 GH 问题中找到的当前解决方法。
在 .storybook
文件夹中创建文件 withSource.js
,内容如下:
import { addons, makeDecorator } from "@storybook/addons";
import kebabCase from "lodash.kebabcase"
import { h, onMounted } from "vue";
// this value doesn't seem to be exported by addons-docs
export const SNIPPET_RENDERED = `storybook/docs/snippet-rendered`;
function templateSourceCode (
templateSource,
args,
argTypes,
replacing = 'v-bind="args"',
) {
const componentArgs = {}
for (const [k, t] of Object.entries(argTypes)) {
const val = args[k]
if (typeof val !== 'undefined' && t.table && t.table.category === 'props' && val !== t.defaultValue) {
componentArgs[k] = val
}
}
const propToSource = (key, val) => {
const type = typeof val
switch (type) {
case "boolean":
return val ? key : ""
case "string":
return `${key}="${val}"`
default:
return `:${key}="${val}"`
}
}
return templateSource.replace(
replacing,
Object.keys(componentArgs)
.map((key) => " " + propToSource(kebabCase(key), args[key]))
.join(""),
)
}
export const withSource = makeDecorator({
name: "withSource",
wrapper: (storyFn, context) => {
const story = storyFn(context);
// this returns a new component that computes the source code when mounted
// and emits an events that is handled by addons-docs
// this approach is based on the vue (2) implementation
// see https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/vue/sourceDecorator.ts
return {
components: {
Story: story,
},
setup() {
onMounted(() => {
try {
// get the story source
const src = context.originalStoryFn().template;
// generate the source code based on the current args
const code = templateSourceCode(
src,
context.args,
context.argTypes
);
const channel = addons.getChannel();
const emitFormattedTemplate = async () => {
const prettier = await import("prettier/standalone");
const prettierHtml = await import("prettier/parser-html");
// emits an event when the transformation is completed
channel.emit(
SNIPPET_RENDERED,
(context || {}).id,
prettier.format(`<template>${code}</template>`, {
parser: "vue",
plugins: [prettierHtml],
htmlWhitespaceSensitivity: "ignore",
})
);
};
setTimeout(emitFormattedTemplate, 0);
} catch (e) {
console.warn("Failed to render code", e);
}
});
return () => h(story);
},
};
},
});
然后将这个装饰器添加到preview.js
:
import { withSource } from './withSource'
...
export const decorators = [
withSource
]