mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-06 07:21:16 +08:00
Merge pull request #19603 from storybookjs/future/CSF3-vue2
Vue2: Improve CSF3 types
This commit is contained in:
commit
065bd82410
@ -49,6 +49,7 @@ module.exports = {
|
|||||||
'/examples/*/src/*/*/*.*',
|
'/examples/*/src/*/*/*.*',
|
||||||
// TODO: Can not get svelte-jester to work, but also not necessary for this test, as it is run by tsc/svelte-check.
|
// TODO: Can not get svelte-jester to work, but also not necessary for this test, as it is run by tsc/svelte-check.
|
||||||
'/renderers/svelte/src/public-types.test.ts',
|
'/renderers/svelte/src/public-types.test.ts',
|
||||||
|
'/renderers/vue/src/public-types.test.ts',
|
||||||
'/renderers/vue3/src/public-types.test.ts',
|
'/renderers/vue3/src/public-types.test.ts',
|
||||||
],
|
],
|
||||||
collectCoverage: false,
|
collectCoverage: false,
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
"*.d.ts"
|
"*.d.ts"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"check": "../../../scripts/node_modules/.bin/tsc --noEmit",
|
"check": "vue-tsc --noEmit",
|
||||||
"prep": "../../../scripts/prepare/bundle.ts"
|
"prep": "../../../scripts/prepare/bundle.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -59,11 +59,13 @@
|
|||||||
"global": "^4.4.0",
|
"global": "^4.4.0",
|
||||||
"react": "16.14.0",
|
"react": "16.14.0",
|
||||||
"react-dom": "16.14.0",
|
"react-dom": "16.14.0",
|
||||||
"ts-dedent": "^2.0.0"
|
"ts-dedent": "^2.0.0",
|
||||||
|
"type-fest": "2.19.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "~4.6.3",
|
"typescript": "~4.6.3",
|
||||||
"vue": "^2.6.12"
|
"vue": "2.6.14",
|
||||||
|
"vue-tsc": "^1.0.9"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@babel/core": "*",
|
"@babel/core": "*",
|
||||||
|
28
code/renderers/vue/src/__tests__/Button.vue
Normal file
28
code/renderers/vue/src/__tests__/Button.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<button type="button" class="classes" @click="onClick" :disabled="disabled">{{ label }}</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import './button.css';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
name: 'my-button',
|
||||||
|
|
||||||
|
props: {
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick(): void {
|
||||||
|
this.$emit('onClick');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
@ -10,7 +10,7 @@ export const WRAPS = 'STORYBOOK_WRAPS';
|
|||||||
|
|
||||||
function prepare(
|
function prepare(
|
||||||
rawStory: StoryFnVueReturnType,
|
rawStory: StoryFnVueReturnType,
|
||||||
innerStory?: VueConstructor,
|
innerStory?: StoryFnVueReturnType,
|
||||||
context?: StoryContext<VueFramework>
|
context?: StoryContext<VueFramework>
|
||||||
): VueConstructor | null {
|
): VueConstructor | null {
|
||||||
let story: ComponentOptions<Vue> | VueConstructor;
|
let story: ComponentOptions<Vue> | VueConstructor;
|
||||||
@ -63,10 +63,10 @@ function prepare(
|
|||||||
export function decorateStory(
|
export function decorateStory(
|
||||||
storyFn: LegacyStoryFn<VueFramework>,
|
storyFn: LegacyStoryFn<VueFramework>,
|
||||||
decorators: DecoratorFunction<VueFramework>[]
|
decorators: DecoratorFunction<VueFramework>[]
|
||||||
): LegacyStoryFn<VueFramework> {
|
) {
|
||||||
return decorators.reduce(
|
return decorators.reduce(
|
||||||
(decorated: LegacyStoryFn<VueFramework>, decorator) => (context: StoryContext<VueFramework>) => {
|
(decorated: LegacyStoryFn<VueFramework>, decorator) => (context: StoryContext<VueFramework>) => {
|
||||||
let story;
|
let story: VueFramework['storyResult'] | undefined;
|
||||||
|
|
||||||
const decoratedStory = decorator((update) => {
|
const decoratedStory = decorator((update) => {
|
||||||
story = decorated({ ...context, ...sanitizeStoryContextUpdate(update) });
|
story = decorated({ ...context, ...sanitizeStoryContextUpdate(update) });
|
||||||
@ -81,10 +81,10 @@ export function decorateStory(
|
|||||||
return story;
|
return story;
|
||||||
}
|
}
|
||||||
|
|
||||||
return prepare(decoratedStory, story as any);
|
return prepare(decoratedStory, story) as VueFramework['storyResult'];
|
||||||
},
|
},
|
||||||
(context) => {
|
(context) => {
|
||||||
return prepare(storyFn(context), null, context);
|
return prepare(storyFn(context), undefined, context) as VueFramework['storyResult'];
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* eslint no-underscore-dangle: ["error", { "allow": ["_vnode"] }] */
|
/* eslint no-underscore-dangle: ["error", { "allow": ["_vnode"] }] */
|
||||||
|
|
||||||
import { ComponentOptions } from 'vue';
|
import type { ComponentOptions, VueConstructor } from 'vue';
|
||||||
import Vue from 'vue/dist/vue';
|
import Vue from 'vue/dist/vue';
|
||||||
import { vnodeToString } from './sourceDecorator';
|
import { vnodeToString } from './sourceDecorator';
|
||||||
|
|
||||||
@ -10,12 +10,13 @@ expect.addSnapshotSerializer({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getVNode = (Component: ComponentOptions<any, any, any>) => {
|
const getVNode = (Component: ComponentOptions<any, any, any>) => {
|
||||||
const vm = new Vue({
|
const vm = new (Vue as unknown as VueConstructor)({
|
||||||
render(h: (c: any) => unknown) {
|
render(h) {
|
||||||
return h(Component);
|
return h(Component);
|
||||||
},
|
},
|
||||||
}).$mount();
|
}).$mount();
|
||||||
|
|
||||||
|
// @ts-expect-error TS says it is called $vnode
|
||||||
return vm.$children[0]._vnode;
|
return vm.$children[0]._vnode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
import { addons } from '@storybook/addons';
|
import { addons } from '@storybook/addons';
|
||||||
import { logger } from '@storybook/client-logger';
|
import { logger } from '@storybook/client-logger';
|
||||||
import type Vue from 'vue';
|
|
||||||
|
|
||||||
import { SourceType, SNIPPET_RENDERED } from '@storybook/docs-tools';
|
import { SourceType, SNIPPET_RENDERED } from '@storybook/docs-tools';
|
||||||
|
import type { ComponentOptions } from 'vue';
|
||||||
|
import type Vue from 'vue';
|
||||||
import type { StoryContext } from '../types';
|
import type { StoryContext } from '../types';
|
||||||
|
|
||||||
export const skipSourceRender = (context: StoryContext) => {
|
export const skipSourceRender = (context: StoryContext) => {
|
||||||
@ -43,6 +43,7 @@ export const sourceDecorator = (storyFn: any, context: StoryContext) => {
|
|||||||
// lifecycle hook.
|
// lifecycle hook.
|
||||||
mounted() {
|
mounted() {
|
||||||
// Theoretically this does not happens but we need to check it.
|
// Theoretically this does not happens but we need to check it.
|
||||||
|
// @ts-expect-error TS says it is called $vnode
|
||||||
if (!this._vnode) {
|
if (!this._vnode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -50,6 +51,7 @@ export const sourceDecorator = (storyFn: any, context: StoryContext) => {
|
|||||||
try {
|
try {
|
||||||
const storyNode = lookupStoryInstance(this, storyComponent);
|
const storyNode = lookupStoryInstance(this, storyComponent);
|
||||||
|
|
||||||
|
// @ts-expect-error TS says it is called $vnode
|
||||||
const code = vnodeToString(storyNode._vnode);
|
const code = vnodeToString(storyNode._vnode);
|
||||||
|
|
||||||
channel.emit(SNIPPET_RENDERED, (context || {}).id, `<template>${code}</template>`, 'vue');
|
channel.emit(SNIPPET_RENDERED, (context || {}).id, `<template>${code}</template>`, 'vue');
|
||||||
@ -58,7 +60,7 @@ export const sourceDecorator = (storyFn: any, context: StoryContext) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: '<story />',
|
template: '<story />',
|
||||||
};
|
} as ComponentOptions<Vue> & ThisType<Vue>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function vnodeToString(vnode: Vue.VNode): string {
|
export function vnodeToString(vnode: Vue.VNode): string {
|
||||||
@ -69,7 +71,7 @@ export function vnodeToString(vnode: Vue.VNode): string {
|
|||||||
...(vnode.data?.attrs ? Object.entries(vnode.data.attrs) : []),
|
...(vnode.data?.attrs ? Object.entries(vnode.data.attrs) : []),
|
||||||
]
|
]
|
||||||
.filter(([name], index, list) => list.findIndex((item) => item[0] === name) === index)
|
.filter(([name], index, list) => list.findIndex((item) => item[0] === name) === index)
|
||||||
.map(([name, value]) => stringifyAttr(name, value))
|
.map(([name, value]) => stringifyAttr(name!, value))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' ');
|
.join(' ');
|
||||||
|
|
||||||
|
178
code/renderers/vue/src/public-types.test.ts
Normal file
178
code/renderers/vue/src/public-types.test.ts
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import { satisfies } from '@storybook/core-common';
|
||||||
|
import { ComponentAnnotations, StoryAnnotations } from '@storybook/csf';
|
||||||
|
import { expectTypeOf } from 'expect-type';
|
||||||
|
import { SetOptional } from 'type-fest';
|
||||||
|
import { Component } from 'vue';
|
||||||
|
import { ExtendedVue, Vue } from 'vue/types/vue';
|
||||||
|
import { DecoratorFn, Meta, StoryObj } from './public-types';
|
||||||
|
import Button from './__tests__/Button.vue';
|
||||||
|
import { VueFramework } from './types';
|
||||||
|
|
||||||
|
describe('Meta', () => {
|
||||||
|
test('Generic parameter of Meta can be a component', () => {
|
||||||
|
const meta: Meta<typeof Button> = {
|
||||||
|
component: Button,
|
||||||
|
args: { label: 'good', disabled: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
expectTypeOf(meta).toEqualTypeOf<
|
||||||
|
ComponentAnnotations<
|
||||||
|
VueFramework,
|
||||||
|
{
|
||||||
|
disabled: boolean;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
>
|
||||||
|
>();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Generic parameter of Meta can be the props of the component', () => {
|
||||||
|
const meta: Meta<{ disabled: boolean; label: string }> = {
|
||||||
|
component: Button,
|
||||||
|
args: { label: 'good', disabled: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
expectTypeOf(meta).toEqualTypeOf<
|
||||||
|
ComponentAnnotations<VueFramework, { disabled: boolean; label: string }>
|
||||||
|
>();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('StoryObj', () => {
|
||||||
|
type ButtonProps = {
|
||||||
|
disabled: boolean;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
test('✅ Required args may be provided partial in meta and the story', () => {
|
||||||
|
const meta = satisfies<Meta<typeof Button>>()({
|
||||||
|
component: Button,
|
||||||
|
args: { label: 'good' },
|
||||||
|
});
|
||||||
|
|
||||||
|
type Actual = StoryObj<typeof meta>;
|
||||||
|
type Expected = StoryAnnotations<VueFramework, ButtonProps, SetOptional<ButtonProps, 'label'>>;
|
||||||
|
expectTypeOf<Actual>().toEqualTypeOf<Expected>();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('❌ The combined shape of meta args and story args must match the required args.', () => {
|
||||||
|
{
|
||||||
|
const meta = satisfies<Meta<typeof Button>>()({ component: Button });
|
||||||
|
|
||||||
|
type Expected = StoryAnnotations<VueFramework, ButtonProps, ButtonProps>;
|
||||||
|
expectTypeOf<StoryObj<typeof meta>>().toEqualTypeOf<Expected>();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const meta = satisfies<Meta<typeof Button>>()({
|
||||||
|
component: Button,
|
||||||
|
args: { label: 'good' },
|
||||||
|
});
|
||||||
|
// @ts-expect-error disabled not provided ❌
|
||||||
|
const Basic: StoryObj<typeof meta> = {};
|
||||||
|
|
||||||
|
type Expected = StoryAnnotations<
|
||||||
|
VueFramework,
|
||||||
|
ButtonProps,
|
||||||
|
SetOptional<ButtonProps, 'label'>
|
||||||
|
>;
|
||||||
|
expectTypeOf(Basic).toEqualTypeOf<Expected>();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const meta = satisfies<Meta<{ label: string; disabled: boolean }>>()({ component: Button });
|
||||||
|
const Basic: StoryObj<typeof meta> = {
|
||||||
|
// @ts-expect-error disabled not provided ❌
|
||||||
|
args: { label: 'good' },
|
||||||
|
};
|
||||||
|
|
||||||
|
type Expected = StoryAnnotations<VueFramework, ButtonProps, ButtonProps>;
|
||||||
|
expectTypeOf(Basic).toEqualTypeOf<Expected>();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Component can be used as generic parameter for StoryObj', () => {
|
||||||
|
expectTypeOf<StoryObj<typeof Button>>().toEqualTypeOf<
|
||||||
|
StoryAnnotations<VueFramework, ButtonProps>
|
||||||
|
>();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
type ThemeData = 'light' | 'dark';
|
||||||
|
|
||||||
|
type ComponentProps<C> = C extends ExtendedVue<any, any, any, any, infer P>
|
||||||
|
? P
|
||||||
|
: C extends Component<infer P>
|
||||||
|
? P
|
||||||
|
: unknown;
|
||||||
|
|
||||||
|
describe('Story args can be inferred', () => {
|
||||||
|
test('Correct args are inferred when type is widened for render function', () => {
|
||||||
|
type Props = ComponentProps<typeof Button> & { theme: ThemeData };
|
||||||
|
|
||||||
|
const meta = satisfies<Meta<Props>>()({
|
||||||
|
component: Button,
|
||||||
|
args: { disabled: false },
|
||||||
|
render: (args) =>
|
||||||
|
Vue.extend({
|
||||||
|
components: { Button },
|
||||||
|
template: `<div>Using the theme: ${args.theme}<Button v-bind="$props"/></div>`,
|
||||||
|
props: Object.keys(args),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const Basic: StoryObj<typeof meta> = { args: { theme: 'light', label: 'good' } };
|
||||||
|
|
||||||
|
type Expected = StoryAnnotations<VueFramework, Props, SetOptional<Props, 'disabled'>>;
|
||||||
|
expectTypeOf(Basic).toEqualTypeOf<Expected>();
|
||||||
|
});
|
||||||
|
|
||||||
|
const withDecorator: DecoratorFn<{ decoratorArg: string }> = (
|
||||||
|
storyFn,
|
||||||
|
{ args: { decoratorArg } }
|
||||||
|
) =>
|
||||||
|
Vue.extend({
|
||||||
|
components: { Story: storyFn() },
|
||||||
|
template: `<div>Decorator: ${decoratorArg}<Story/></div>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Correct args are inferred when type is widened for decorators', () => {
|
||||||
|
type Props = ComponentProps<typeof Button> & { decoratorArg: string };
|
||||||
|
|
||||||
|
const meta = satisfies<Meta<Props>>()({
|
||||||
|
component: Button,
|
||||||
|
args: { disabled: false },
|
||||||
|
decorators: [withDecorator],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Basic: StoryObj<typeof meta> = { args: { decoratorArg: 'title', label: 'good' } };
|
||||||
|
|
||||||
|
type Expected = StoryAnnotations<VueFramework, Props, SetOptional<Props, 'disabled'>>;
|
||||||
|
expectTypeOf(Basic).toEqualTypeOf<Expected>();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Correct args are inferred when type is widened for multiple decorators', () => {
|
||||||
|
type Props = ComponentProps<typeof Button> & { decoratorArg: string; decoratorArg2: string };
|
||||||
|
|
||||||
|
const secondDecorator: DecoratorFn<{ decoratorArg2: string }> = (
|
||||||
|
storyFn,
|
||||||
|
{ args: { decoratorArg2 } }
|
||||||
|
) => {
|
||||||
|
return Vue.extend({
|
||||||
|
components: { Story: storyFn() },
|
||||||
|
template: `<div>Decorator: ${decoratorArg2}<Story/></div>`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta = satisfies<Meta<Props>>()({
|
||||||
|
component: Button,
|
||||||
|
args: { disabled: false },
|
||||||
|
decorators: [withDecorator, secondDecorator],
|
||||||
|
});
|
||||||
|
|
||||||
|
const Basic: StoryObj<typeof meta> = {
|
||||||
|
args: { decoratorArg: '', decoratorArg2: '', label: 'good' },
|
||||||
|
};
|
||||||
|
|
||||||
|
type Expected = StoryAnnotations<VueFramework, Props, SetOptional<Props, 'disabled'>>;
|
||||||
|
expectTypeOf(Basic).toEqualTypeOf<Expected>();
|
||||||
|
});
|
||||||
|
});
|
@ -1,9 +1,15 @@
|
|||||||
import type {
|
import type {
|
||||||
Args,
|
|
||||||
ComponentAnnotations,
|
|
||||||
StoryAnnotations,
|
|
||||||
AnnotatedStoryFn,
|
AnnotatedStoryFn,
|
||||||
|
Args,
|
||||||
|
ArgsFromMeta,
|
||||||
|
ArgsStoryFn,
|
||||||
|
ComponentAnnotations,
|
||||||
|
DecoratorFunction,
|
||||||
|
StoryAnnotations,
|
||||||
} from '@storybook/csf';
|
} from '@storybook/csf';
|
||||||
|
import { SetOptional, Simplify } from 'type-fest';
|
||||||
|
import { Component } from 'vue';
|
||||||
|
import { ExtendedVue } from 'vue/types/vue';
|
||||||
import { VueFramework } from './types';
|
import { VueFramework } from './types';
|
||||||
|
|
||||||
export type { Args, ArgTypes, Parameters, StoryContext } from '@storybook/csf';
|
export type { Args, ArgTypes, Parameters, StoryContext } from '@storybook/csf';
|
||||||
@ -13,7 +19,12 @@ export type { Args, ArgTypes, Parameters, StoryContext } from '@storybook/csf';
|
|||||||
*
|
*
|
||||||
* @see [Default export](https://storybook.js.org/docs/formats/component-story-format/#default-export)
|
* @see [Default export](https://storybook.js.org/docs/formats/component-story-format/#default-export)
|
||||||
*/
|
*/
|
||||||
export type Meta<TArgs = Args> = ComponentAnnotations<VueFramework, TArgs>;
|
export type Meta<CmpOrArgs = Args> = CmpOrArgs extends Component<any>
|
||||||
|
? ComponentAnnotations<
|
||||||
|
VueFramework,
|
||||||
|
unknown extends ComponentProps<CmpOrArgs> ? CmpOrArgs : ComponentProps<CmpOrArgs>
|
||||||
|
>
|
||||||
|
: ComponentAnnotations<VueFramework, CmpOrArgs>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Story function that represents a CSFv2 component example.
|
* Story function that represents a CSFv2 component example.
|
||||||
@ -27,11 +38,36 @@ export type StoryFn<TArgs = Args> = AnnotatedStoryFn<VueFramework, TArgs>;
|
|||||||
*
|
*
|
||||||
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
|
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
|
||||||
*/
|
*/
|
||||||
export type StoryObj<TArgs = Args> = StoryAnnotations<VueFramework, TArgs>;
|
export type StoryObj<MetaOrCmpOrArgs = Args> = MetaOrCmpOrArgs extends {
|
||||||
|
render?: ArgsStoryFn<VueFramework, any>;
|
||||||
|
component?: infer C;
|
||||||
|
args?: infer DefaultArgs;
|
||||||
|
}
|
||||||
|
? MetaOrCmpOrArgs extends Component<any>
|
||||||
|
? StoryAnnotations<VueFramework, ComponentProps<MetaOrCmpOrArgs>>
|
||||||
|
: Simplify<ComponentProps<C> & ArgsFromMeta<VueFramework, MetaOrCmpOrArgs>> extends infer TArgs
|
||||||
|
? StoryAnnotations<
|
||||||
|
VueFramework,
|
||||||
|
TArgs,
|
||||||
|
SetOptional<TArgs, Extract<keyof TArgs, keyof DefaultArgs>>
|
||||||
|
>
|
||||||
|
: never
|
||||||
|
: MetaOrCmpOrArgs extends Component<any>
|
||||||
|
? StoryAnnotations<VueFramework, ComponentProps<MetaOrCmpOrArgs>>
|
||||||
|
: StoryAnnotations<VueFramework, MetaOrCmpOrArgs>;
|
||||||
|
|
||||||
|
type ComponentProps<C> = C extends ExtendedVue<any, any, any, any, infer P>
|
||||||
|
? P
|
||||||
|
: C extends Component<any, any, any, infer P>
|
||||||
|
? P
|
||||||
|
: unknown;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Story function that represents a CSFv3 component example.
|
* @deprecated Use `StoryFn` instead.
|
||||||
|
* Story function that represents a CSFv2 component example.
|
||||||
*
|
*
|
||||||
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
|
* @see [Named Story exports](https://storybook.js.org/docs/formats/component-story-format/#named-story-exports)
|
||||||
*/
|
*/
|
||||||
export type Story<TArgs = Args> = StoryObj<TArgs>;
|
export type Story<TArgs = Args> = StoryFn<TArgs>;
|
||||||
|
|
||||||
|
export type DecoratorFn<TArgs = Args> = DecoratorFunction<VueFramework, TArgs>;
|
||||||
|
@ -18,20 +18,19 @@ type Instance = CombinedVueInstance<
|
|||||||
},
|
},
|
||||||
object,
|
object,
|
||||||
object,
|
object,
|
||||||
Record<never, any>,
|
Record<never, any>
|
||||||
unknown
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const getRoot = (domElement: Element): Instance => {
|
const getRoot = (domElement: Element): Instance => {
|
||||||
if (map.has(domElement)) {
|
const cachedInstance = map.get(domElement);
|
||||||
return map.get(domElement);
|
if (cachedInstance != null) return cachedInstance;
|
||||||
}
|
|
||||||
|
|
||||||
// Create a dummy "target" underneath #storybook-root
|
// Create a dummy "target" underneath #storybook-root
|
||||||
// that Vue2 will replace on first render with #storybook-vue-root
|
// that Vue2 will replace on first render with #storybook-vue-root
|
||||||
const target = document.createElement('div');
|
const target = document.createElement('div');
|
||||||
domElement.appendChild(target);
|
domElement.appendChild(target);
|
||||||
|
|
||||||
const instance = new Vue({
|
const instance: Instance = new Vue({
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
map.delete(domElement);
|
map.delete(domElement);
|
||||||
},
|
},
|
||||||
@ -41,12 +40,12 @@ const getRoot = (domElement: Element): Instance => {
|
|||||||
[VALUES]: {},
|
[VALUES]: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// @ts-expect-error What's going on here?
|
// @ts-expect-error What's going on here? (TS says that we should not return an array here, but the `h` directly)
|
||||||
render(h) {
|
render(h) {
|
||||||
map.set(domElement, instance);
|
map.set(domElement, instance);
|
||||||
return this[COMPONENT] ? [h(this[COMPONENT])] : undefined;
|
return this[COMPONENT] ? [h(this[COMPONENT])] : undefined;
|
||||||
},
|
},
|
||||||
}) as Instance;
|
});
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
};
|
};
|
||||||
@ -100,7 +99,7 @@ export function renderToDOM(
|
|||||||
Vue.config.errorHandler = showException;
|
Vue.config.errorHandler = showException;
|
||||||
const element = storyFn();
|
const element = storyFn();
|
||||||
|
|
||||||
let mountTarget: Element;
|
let mountTarget: Element | null;
|
||||||
|
|
||||||
// Vue2 mount always replaces the mount target with Vue-generated DOM.
|
// Vue2 mount always replaces the mount target with Vue-generated DOM.
|
||||||
// https://v2.vuejs.org/v2/api/#el:~:text=replaced%20with%20Vue%2Dgenerated%20DOM
|
// https://v2.vuejs.org/v2/api/#el:~:text=replaced%20with%20Vue%2Dgenerated%20DOM
|
||||||
@ -134,7 +133,7 @@ export function renderToDOM(
|
|||||||
root[VALUES] = { ...element.options[VALUES] };
|
root[VALUES] = { ...element.options[VALUES] };
|
||||||
|
|
||||||
if (!map.has(domElement)) {
|
if (!map.has(domElement)) {
|
||||||
root.$mount(mountTarget);
|
root.$mount(mountTarget ?? undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
showMain();
|
showMain();
|
||||||
|
@ -8,8 +8,9 @@ export interface ShowErrorArgs {
|
|||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: some vue expert needs to look at this
|
export type StoryFnVueReturnType =
|
||||||
export type StoryFnVueReturnType = string | Component;
|
| Component<any, any, any, any>
|
||||||
|
| AsyncComponent<any, any, any, any>;
|
||||||
|
|
||||||
export type StoryContext = StoryContextBase<VueFramework>;
|
export type StoryContext = StoryContextBase<VueFramework>;
|
||||||
|
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
{
|
{
|
||||||
"extends": "../../tsconfig.json",
|
"extends": "../../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"resolveJsonModule": true
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"vueCompilerOptions": {
|
||||||
"exclude": ["src/**/*.test.*"]
|
"target": 2
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "src/**/*.vue"],
|
||||||
|
"exclude": []
|
||||||
}
|
}
|
||||||
|
@ -8073,8 +8073,10 @@ __metadata:
|
|||||||
react: 16.14.0
|
react: 16.14.0
|
||||||
react-dom: 16.14.0
|
react-dom: 16.14.0
|
||||||
ts-dedent: ^2.0.0
|
ts-dedent: ^2.0.0
|
||||||
|
type-fest: 2.19.0
|
||||||
typescript: ~4.6.3
|
typescript: ~4.6.3
|
||||||
vue: ^2.6.12
|
vue: 2.6.14
|
||||||
|
vue-tsc: ^1.0.9
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@babel/core": "*"
|
"@babel/core": "*"
|
||||||
babel-loader: ^7.0.0 || ^8.0.0
|
babel-loader: ^7.0.0 || ^8.0.0
|
||||||
@ -9707,58 +9709,58 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@volar/language-core@npm:1.0.8":
|
"@volar/language-core@npm:1.0.9":
|
||||||
version: 1.0.8
|
version: 1.0.9
|
||||||
resolution: "@volar/language-core@npm:1.0.8"
|
resolution: "@volar/language-core@npm:1.0.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/source-map": 1.0.8
|
"@volar/source-map": 1.0.9
|
||||||
"@vue/reactivity": ^3.2.40
|
"@vue/reactivity": ^3.2.40
|
||||||
muggle-string: ^0.1.0
|
muggle-string: ^0.1.0
|
||||||
checksum: e190b36e427075ac26f1b93064dc5982f6cd45c5fde2b32604db329c4ec4352f87a45b88a37fff3b2663d3f4ecb6ba63155d69f45374622492e6b99073c6f2a7
|
checksum: 8e13fbe3fdf6443c92348de686affe9f224848dba6b61f6beb1b98d1041a5b0073e10fb2288904f01b45162dfc8d5446e09cc4ca90de4aafafcde4ada843f8d7
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@volar/source-map@npm:1.0.8":
|
"@volar/source-map@npm:1.0.9":
|
||||||
version: 1.0.8
|
version: 1.0.9
|
||||||
resolution: "@volar/source-map@npm:1.0.8"
|
resolution: "@volar/source-map@npm:1.0.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
muggle-string: ^0.1.0
|
muggle-string: ^0.1.0
|
||||||
checksum: 65c8566c3de6a5e23a80c2df2f63b1c06d2eb1f5af2a6f20cd1c81029626f230c49e514acb7e8907c22e488b788c2da56ff8f44aa7c0b02be01bae5fa1c85ecb
|
checksum: a597ced036a8be921c90b74af1fcb8d1c12d07d3f6678020d185b9e27602519004772cc07c99f9617beef291d898533c3cf41654080fa3eacd5065a0bc28c54d
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@volar/typescript@npm:1.0.8":
|
"@volar/typescript@npm:1.0.9":
|
||||||
version: 1.0.8
|
version: 1.0.9
|
||||||
resolution: "@volar/typescript@npm:1.0.8"
|
resolution: "@volar/typescript@npm:1.0.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/language-core": 1.0.8
|
"@volar/language-core": 1.0.9
|
||||||
checksum: e955d741116a8cac06cff8dd50bac576f8d65df54734cb62dcc422c4de910e3efd189783423c5005d2b609cc5ae519fa816d6b4cf51b9947e3b6cb87522f9a56
|
checksum: e4c2ddea461c474324352bebc31a1b87955abb0792764730d1559af12ee896e51e511756bbc56d805644c72981c9f8569913f75b995edff101cd86df3cdc1816
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@volar/vue-language-core@npm:1.0.8":
|
"@volar/vue-language-core@npm:1.0.9":
|
||||||
version: 1.0.8
|
version: 1.0.9
|
||||||
resolution: "@volar/vue-language-core@npm:1.0.8"
|
resolution: "@volar/vue-language-core@npm:1.0.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/language-core": 1.0.8
|
"@volar/language-core": 1.0.9
|
||||||
"@volar/source-map": 1.0.8
|
"@volar/source-map": 1.0.9
|
||||||
"@vue/compiler-dom": ^3.2.40
|
"@vue/compiler-dom": ^3.2.40
|
||||||
"@vue/compiler-sfc": ^3.2.40
|
"@vue/compiler-sfc": ^3.2.40
|
||||||
"@vue/reactivity": ^3.2.40
|
"@vue/reactivity": ^3.2.40
|
||||||
"@vue/shared": ^3.2.40
|
"@vue/shared": ^3.2.40
|
||||||
minimatch: ^5.1.0
|
minimatch: ^5.1.0
|
||||||
vue-template-compiler: ^2.7.10
|
vue-template-compiler: ^2.7.10
|
||||||
checksum: 9d927267ea3de0f96513c4ff4b09e8dfacca676c3e628fac4fe4327ac497a14850a8a1dcab709ea443eb998269faecf18d38c25124d6c2e70fb5d54574959092
|
checksum: e61f82d55c5bf70a828ee4f1d2d1cd97fb12693a8ce1a7c43ebe789bbb18fc371aaaf61ba7c4e6addb7972f7f37245e25b247222e5fd73f1d60a29bb9d846857
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@volar/vue-typescript@npm:1.0.8":
|
"@volar/vue-typescript@npm:1.0.9":
|
||||||
version: 1.0.8
|
version: 1.0.9
|
||||||
resolution: "@volar/vue-typescript@npm:1.0.8"
|
resolution: "@volar/vue-typescript@npm:1.0.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/typescript": 1.0.8
|
"@volar/typescript": 1.0.9
|
||||||
"@volar/vue-language-core": 1.0.8
|
"@volar/vue-language-core": 1.0.9
|
||||||
checksum: 7d51290ef6e3e9d3adfc9584296e7264921fda6e7ade4a3964f8679a3d4a4038e8ae05a8532d2640a09ff78a8f4158f6cf6a759a28b716fe58adb20a4ef7a548
|
checksum: 36dea3950e7433892c49af3a439ce2b63cbeefe462f68c9463b8d8dd4d20e44544e9a30ff4ed55ee941418dec5ce641f09566069b08a4171c196f8eff5b6da47
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -35700,17 +35702,24 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"vue-tsc@npm:^1.0.8":
|
"vue-tsc@npm:^1.0.8, vue-tsc@npm:^1.0.9":
|
||||||
version: 1.0.8
|
version: 1.0.9
|
||||||
resolution: "vue-tsc@npm:1.0.8"
|
resolution: "vue-tsc@npm:1.0.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/vue-language-core": 1.0.8
|
"@volar/vue-language-core": 1.0.9
|
||||||
"@volar/vue-typescript": 1.0.8
|
"@volar/vue-typescript": 1.0.9
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: "*"
|
typescript: "*"
|
||||||
bin:
|
bin:
|
||||||
vue-tsc: bin/vue-tsc.js
|
vue-tsc: bin/vue-tsc.js
|
||||||
checksum: e9ea48ef84249f842e910c8d1dab7e6b681af77714f2c4b5c7cc0810e22bb40d02bcd0701c3e30d8e52d5bd71d2a547204efa53f5caa793c96647a3b6bcf7f90
|
checksum: b1a446cd3a88220d7e996c54afb8e1932eeabcd8efdcc0fcb8dd06e83833a70aa835373b687c6bf35a7d35cfcd79c776737a13eecc8faf1b237d695a5abf1433
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"vue@npm:2.6.14":
|
||||||
|
version: 2.6.14
|
||||||
|
resolution: "vue@npm:2.6.14"
|
||||||
|
checksum: efbe26ccc7c1bd025b88e464ebc81217b92350a77b98049122a46ac2242e249719f930d3914e2efdeaaa521a51e6e6b1cb9ffbf95b4835ed94dc92efb481040f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user