@storybook/vue3
Version:
Storybook Vue 3 renderer: Develop, document, and test UI components in isolation
264 lines (256 loc) • 11.6 kB
JavaScript
import {
__export
} from "./chunk-4BE7D4DS.js";
// src/entry-preview.ts
var entry_preview_exports = {};
__export(entry_preview_exports, {
applyDecorators: () => decorateStory,
argTypesEnhancers: () => argTypesEnhancers,
mount: () => mount,
parameters: () => parameters,
render: () => render,
renderToCanvas: () => renderToCanvas
});
import { enhanceArgTypes, extractComponentDescription } from "storybook/internal/docs-tools";
// src/extractArgTypes.ts
import {
convert,
extractComponentProps,
hasDocgen
} from "storybook/internal/docs-tools";
var ARG_TYPE_SECTIONS = ["props", "events", "slots", "exposed", "expose"], extractArgTypes = (component) => {
if (!hasDocgen(component))
return null;
let usedDocgenPlugin = "exposed" in component.__docgenInfo ? "vue-component-meta" : "vue-docgen-api", argTypes = {};
return ARG_TYPE_SECTIONS.forEach((section) => {
extractComponentProps(component, section).forEach((extractedProp) => {
let argType;
if (usedDocgenPlugin === "vue-docgen-api") {
let docgenInfo = extractedProp.docgenInfo;
argType = extractFromVueDocgenApi(docgenInfo, section, extractedProp);
} else {
let docgenInfo = extractedProp.docgenInfo;
argType = extractFromVueComponentMeta(docgenInfo, section);
}
if (!argType || argTypes[argType.name])
return;
["events", "expose", "exposed"].includes(section) && (argType.control = { disable: !0 }), argTypes[argType.name] = argType;
});
}), argTypes;
}, extractFromVueDocgenApi = (docgenInfo, section, extractedProp) => {
let type, sbType;
if (section === "events" && (type = docgenInfo.type?.names.join(), sbType = { name: "other", value: type ?? "", required: !1 }), section === "slots") {
let slotBindings = docgenInfo.bindings?.filter((binding) => !!binding.name).map((binding) => `${binding.name}: ${binding.type?.name ?? "unknown"}`).join("; ");
type = slotBindings ? `{ ${slotBindings} }` : void 0, sbType = { name: "other", value: type ?? "", required: !1 };
}
if (section === "props") {
let propInfo = docgenInfo;
if (type = propInfo.type?.name, sbType = extractedProp ? convert(extractedProp.docgenInfo) : { name: "other", value: type }, propInfo.type && "elements" in propInfo.type && Array.isArray(propInfo.type.elements) && propInfo.type.elements.length > 0) {
let elements = propInfo.type.elements.map((i) => i.name);
type === "Array" && (type = `${elements.length === 1 ? elements[0] : `(${elements.join(" | ")})`}[]`), type === "union" ? type = elements.join(" | ") : type === "intersection" && (type = elements.join(" & "));
}
}
let required = "required" in docgenInfo ? docgenInfo.required ?? !1 : !1;
return {
name: docgenInfo.name,
description: docgenInfo.description,
type: sbType ? { ...sbType, required } : { name: "other", value: type ?? "" },
table: {
type: type ? { summary: type } : void 0,
defaultValue: extractedProp?.propDef.defaultValue ?? void 0,
jsDocTags: extractedProp?.propDef.jsDocTags,
category: section
}
};
}, extractFromVueComponentMeta = (docgenInfo, section) => {
if ("global" in docgenInfo && docgenInfo.global)
return;
let tableType = { summary: docgenInfo.type.replace(" | undefined", "") };
if (section === "props") {
let propInfo = docgenInfo, defaultValue = propInfo.default ? { summary: propInfo.default } : void 0;
return {
name: propInfo.name,
description: formatDescriptionWithTags(propInfo.description, propInfo.tags),
defaultValue,
type: convertVueComponentMetaProp(propInfo),
table: {
type: tableType,
defaultValue,
category: section
}
};
} else
return {
name: docgenInfo.name,
description: "description" in docgenInfo ? docgenInfo.description : "",
type: { name: "other", value: docgenInfo.type },
table: { type: tableType, category: section }
};
}, convertVueComponentMetaProp = (propInfo) => {
let schema = propInfo.schema, required = propInfo.required, fallbackSbType = { name: "other", value: propInfo.type, required }, KNOWN_SCHEMAS = ["string", "number", "function", "boolean", "symbol"];
if (typeof schema == "string")
return KNOWN_SCHEMAS.includes(schema) ? { name: schema, required } : fallbackSbType;
switch (schema.kind) {
case "enum": {
let definedSchemas = schema.schema?.filter((item) => item !== "undefined") ?? [];
return isBooleanSchema(definedSchemas) ? { name: "boolean", required } : isLiteralUnionSchema(definedSchemas) || isEnumSchema(definedSchemas) ? { name: "enum", value: definedSchemas.map((literal) => literal.replace(/"/g, "")), required } : definedSchemas.length === 1 ? convertVueComponentMetaProp({
schema: definedSchemas[0],
type: propInfo.type,
required
}) : (definedSchemas.length > 2 && definedSchemas.includes("true") && definedSchemas.includes("false") && (definedSchemas = definedSchemas.filter((i) => i !== "true" && i !== "false"), definedSchemas.push("boolean")), {
name: "union",
value: definedSchemas.map((i) => convertVueComponentMetaProp(typeof i == "object" ? {
schema: i,
type: i.type,
required: !1
} : { schema: i, type: i, required: !1 })),
required
});
}
case "array": {
let definedSchemas = schema.schema?.filter((item) => item !== "undefined") ?? [];
return definedSchemas.length === 0 ? fallbackSbType : definedSchemas.length === 1 ? {
name: "array",
value: convertVueComponentMetaProp({
schema: definedSchemas[0],
type: propInfo.type,
required: !1
}),
required
} : {
name: "union",
value: definedSchemas.map((i) => convertVueComponentMetaProp(typeof i == "object" ? {
schema: i,
type: i.type,
required: !1
} : { schema: i, type: i, required: !1 })),
required
};
}
case "object":
return {
name: "object",
// while Storybook generates simple JSON object controls, nested schemas don't have specialized controls
// so we don't need to recursively map the object schema here
value: {},
required
};
default:
return fallbackSbType;
}
}, formatDescriptionWithTags = (description, tags) => !tags?.length || !description ? description ?? "" : `${tags.map((tag) => `@${tag.name}: ${tag.text}`).join("<br>")}<br><br>${description}`, isLiteralUnionSchema = (schemas) => schemas.every(
(schema) => typeof schema == "string" && schema.startsWith('"') && schema.endsWith('"')
), isEnumSchema = (schemas) => schemas.every((schema) => typeof schema == "string" && schema.includes(".")), isBooleanSchema = (schemas) => schemas.length === 2 && schemas.includes("true") && schemas.includes("false");
// src/render.ts
import { createApp, h, isReactive, isVNode, reactive } from "vue";
var render = (props, context) => {
let { id, component: Component } = context;
if (!Component)
throw new Error(
`Unable to render story ${id} as the component annotation is missing from the default export`
);
return () => h(Component, props, getSlots(props, context));
}, setup = (fn) => {
globalThis.PLUGINS_SETUP_FUNCTIONS ??= /* @__PURE__ */ new Set(), globalThis.PLUGINS_SETUP_FUNCTIONS.add(fn);
}, runSetupFunctions = async (app, storyContext) => {
globalThis && globalThis.PLUGINS_SETUP_FUNCTIONS && await Promise.all([...globalThis.PLUGINS_SETUP_FUNCTIONS].map((fn) => fn(app, storyContext)));
}, map = /* @__PURE__ */ new Map();
async function renderToCanvas({ storyFn, forceRemount, showMain, showException, storyContext, id }, canvasElement) {
let existingApp = map.get(canvasElement);
if (existingApp && !forceRemount) {
let element = storyFn(), args = getArgs(element, storyContext);
return updateArgs(existingApp.reactiveArgs, args), () => {
teardown(existingApp.vueApp, canvasElement);
};
}
existingApp && forceRemount && teardown(existingApp.vueApp, canvasElement);
let vueApp = createApp({
setup() {
storyContext.args = reactive(storyContext.args);
let rootElement = storyFn(), args = getArgs(rootElement, storyContext), appState = {
vueApp,
reactiveArgs: reactive(args)
};
return map.set(canvasElement, appState), () => h(rootElement);
}
});
return vueApp.config.errorHandler = (e, instance, info) => {
window.__STORYBOOK_PREVIEW__?.storyRenders.some(
(renderer) => renderer.id === id && renderer.phase === "playing"
) ? setTimeout(() => {
throw e;
}, 0) : showException(e);
}, await runSetupFunctions(vueApp, storyContext), vueApp.mount(canvasElement), showMain(), () => {
teardown(vueApp, canvasElement);
};
}
function getSlots(props, context) {
let { argTypes } = context, slots = Object.entries(props).filter(([key]) => argTypes[key]?.table?.category === "slots").map(([key, value]) => [key, typeof value == "function" ? value : () => value]);
return Object.fromEntries(slots);
}
function getArgs(element, storyContext) {
return element.props && isVNode(element) ? element.props : storyContext.args;
}
function updateArgs(reactiveArgs, nextArgs) {
if (Object.keys(nextArgs).length === 0)
return;
let currentArgs = isReactive(reactiveArgs) ? reactiveArgs : reactive(reactiveArgs);
Object.keys(currentArgs).forEach((key) => {
key in nextArgs || delete currentArgs[key];
}), Object.assign(currentArgs, nextArgs);
}
function teardown(storybookApp, canvasElement) {
storybookApp?.unmount(), map.has(canvasElement) && map.delete(canvasElement);
}
// src/decorateStory.ts
import { sanitizeStoryContextUpdate } from "storybook/preview-api";
import { h as h2 } from "vue";
function normalizeFunctionalComponent(options) {
return typeof options == "function" ? { render: options, name: options.name } : options;
}
function prepare(rawStory, innerStory) {
let story = rawStory;
return story === null ? null : typeof story == "function" ? story : innerStory ? {
// Normalize so we can always spread an object
...normalizeFunctionalComponent(story),
components: { ...story.components || {}, story: innerStory }
} : {
render() {
return h2(story);
}
};
}
function decorateStory(storyFn, decorators) {
return decorators.reduce(
(decorated, decorator) => (context) => {
let story, decoratedStory = decorator((update) => {
let sanitizedUpdate = sanitizeStoryContextUpdate(update);
return update && (sanitizedUpdate.args = Object.assign(context.args, sanitizedUpdate.args)), story = decorated({ ...context, ...sanitizedUpdate }), story;
}, context);
return story || (story = decorated(context)), decoratedStory === story ? story : prepare(decoratedStory, () => h2(story));
},
(context) => prepare(storyFn(context))
);
}
// src/mount.ts
import { h as h3 } from "vue";
var mount = (context) => async (Component, options) => (Component && (context.originalStoryFn = () => () => h3(Component, options?.props, options?.slots)), await context.renderToCanvas(), context.canvas);
// src/entry-preview.ts
var parameters = {
renderer: "vue3",
docs: {
story: { inline: !0 },
extractArgTypes,
extractComponentDescription
}
}, argTypesEnhancers = [enhanceArgTypes];
export {
render,
setup,
renderToCanvas,
decorateStory,
mount,
parameters,
argTypesEnhancers,
entry_preview_exports
};