@storybook/vue3 7.1.0-alpha.3 → 7.1.0-alpha.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-HSFPF76R.mjs +6 -0
- package/dist/config.d.ts +6 -17
- package/dist/config.js +25 -44
- package/dist/config.mjs +32 -43
- package/dist/index.d.ts +10 -11
- package/dist/index.js +1 -1
- package/dist/index.mjs +8 -1
- package/dist/render-32b7dd3f.d.ts +148 -0
- package/package.json +15 -14
- package/src/typings.d.ts +1 -0
- package/template/cli/js/Header.vue +1 -1
- package/template/cli/js/Page.vue +1 -1
- package/template/cli/ts-3-8/Header.vue +1 -1
- package/template/cli/ts-3-8/Page.vue +8 -5
- package/template/cli/ts-4-9/Header.vue +1 -1
- package/template/cli/ts-4-9/Page.vue +8 -5
- package/template/stories/preview.js +29 -4
- package/template/stories_vue3-vite-default-ts/BaseLayout.vue +18 -0
- package/template/stories_vue3-vite-default-ts/CustomRenderFunctionalComponent.stories.ts +32 -0
- package/template/stories_vue3-vite-default-ts/CustomRenderOptionsArgsFromData.stories.ts +45 -0
- package/template/stories_vue3-vite-default-ts/GlobalSetup.stories.ts +52 -0
- package/template/stories_vue3-vite-default-ts/GlobalSetup.vue +6 -0
- package/template/{stories/GlobalUsage.stories.js → stories_vue3-vite-default-ts/GlobalUsage.stories.ts} +1 -1
- package/template/stories_vue3-vite-default-ts/GlobalUsage.vue +3 -0
- package/template/stories_vue3-vite-default-ts/MySlotComponent.vue +12 -0
- package/template/{stories → stories_vue3-vite-default-ts}/OverrideArgs.stories.js +3 -1
- package/template/{stories → stories_vue3-vite-default-ts}/OverrideArgs.vue +3 -3
- package/template/stories_vue3-vite-default-ts/ReactiveArgs.stories.ts +137 -0
- package/template/stories_vue3-vite-default-ts/ReactiveDecorators.stories.ts +88 -0
- package/template/stories_vue3-vite-default-ts/ReactiveSlots.stories.ts +127 -0
- package/template/stories_vue3-vite-default-ts/Reactivity.vue +44 -0
- package/template/stories_vue3-vite-default-ts/ScopedSlots.stories.ts +81 -0
- package/template/stories_vue3-vite-default-ts/SourceDecorator.stories.ts +46 -0
- package/template/stories_vue3-vite-default-ts/decorators.stories.ts +84 -0
- package/template/stories_vue3-vite-default-ts/preview.ts +12 -0
- package/dist/chunk-2GDW2BFM.mjs +0 -1
- package/dist/render-c842c5d5.d.ts +0 -14
- package/template/stories/GlobalUsage.vue +0 -3
- package/template/stories/ReactiveArgs.stories.js +0 -44
- package/template/stories/decorators.stories.js +0 -66
- /package/template/{stories → stories_vue3-vite-default-ts}/ReactiveArgs.vue +0 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
<!-- <MyComponent> template -->
|
2
|
+
<script setup lang="ts">
|
3
|
+
const props = defineProps({
|
4
|
+
label: String,
|
5
|
+
year: Number,
|
6
|
+
});
|
7
|
+
</script>
|
8
|
+
<template>
|
9
|
+
<div data-testid="scoped-slot">
|
10
|
+
<slot :text="`Hello ${props.label} from the slot`" :year="props.year"></slot>
|
11
|
+
</div>
|
12
|
+
</template>
|
@@ -10,7 +10,7 @@ const icons = {
|
|
10
10
|
},
|
11
11
|
};
|
12
12
|
|
13
|
-
|
13
|
+
const meta = {
|
14
14
|
component: OverrideArgs,
|
15
15
|
argTypes: {
|
16
16
|
// To show that other props are passed through
|
@@ -39,4 +39,6 @@ export default {
|
|
39
39
|
},
|
40
40
|
};
|
41
41
|
|
42
|
+
export default meta;
|
43
|
+
|
42
44
|
export const TestOne = {};
|
@@ -5,8 +5,8 @@
|
|
5
5
|
</button>
|
6
6
|
</template>
|
7
7
|
|
8
|
-
<script lang="
|
9
|
-
import {
|
8
|
+
<script lang="ts">
|
9
|
+
import { computed } from 'vue';
|
10
10
|
|
11
11
|
export default {
|
12
12
|
name: 'override-args',
|
@@ -22,7 +22,7 @@ export default {
|
|
22
22
|
},
|
23
23
|
},
|
24
24
|
|
25
|
-
|
25
|
+
|
26
26
|
setup(props, { emit }) {
|
27
27
|
const classes = {
|
28
28
|
'storybook-button': true,
|
@@ -0,0 +1,137 @@
|
|
1
|
+
import { expect } from '@storybook/jest';
|
2
|
+
import { global as globalThis } from '@storybook/global';
|
3
|
+
import type { Meta, StoryObj, StoryFn } from '@storybook/vue3';
|
4
|
+
import { within, userEvent } from '@storybook/testing-library';
|
5
|
+
import { UPDATE_STORY_ARGS, STORY_ARGS_UPDATED, RESET_STORY_ARGS } from '@storybook/core-events';
|
6
|
+
|
7
|
+
import ReactiveArgs from './ReactiveArgs.vue';
|
8
|
+
|
9
|
+
const meta = {
|
10
|
+
component: ReactiveArgs,
|
11
|
+
argTypes: {
|
12
|
+
// To show that other props are passed through
|
13
|
+
backgroundColor: { control: 'color' },
|
14
|
+
},
|
15
|
+
} satisfies Meta<typeof ReactiveArgs>;
|
16
|
+
|
17
|
+
export default meta;
|
18
|
+
|
19
|
+
type Story = StoryObj<typeof meta>;
|
20
|
+
|
21
|
+
export const ReactiveTest: Story = {
|
22
|
+
args: {
|
23
|
+
label: 'Button',
|
24
|
+
},
|
25
|
+
// test that args are updated correctly in rective mode
|
26
|
+
play: async ({ canvasElement, id }) => {
|
27
|
+
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
|
28
|
+
const canvas = within(canvasElement);
|
29
|
+
|
30
|
+
await channel.emit(RESET_STORY_ARGS, { storyId: id });
|
31
|
+
await new Promise((resolve) => {
|
32
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
33
|
+
});
|
34
|
+
const reactiveButton = await canvas.getByRole('button');
|
35
|
+
await expect(reactiveButton).toHaveTextContent('Button 0');
|
36
|
+
|
37
|
+
await userEvent.click(reactiveButton); // click to update the label to increment the count + 1
|
38
|
+
await channel.emit(UPDATE_STORY_ARGS, {
|
39
|
+
storyId: id,
|
40
|
+
updatedArgs: { label: 'updated' },
|
41
|
+
});
|
42
|
+
await new Promise((resolve) => {
|
43
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
44
|
+
});
|
45
|
+
await expect(canvas.getByRole('button')).toHaveTextContent('updated 1');
|
46
|
+
|
47
|
+
await userEvent.click(reactiveButton); // click to update the label to increment the count + 1
|
48
|
+
await expect(reactiveButton).toHaveTextContent('updated 2');
|
49
|
+
},
|
50
|
+
};
|
51
|
+
|
52
|
+
export const ReactiveHtmlWrapper: Story = {
|
53
|
+
args: { label: 'Wrapped Button' },
|
54
|
+
|
55
|
+
decorators: [
|
56
|
+
() => ({
|
57
|
+
template: `
|
58
|
+
<div style="border: 5px solid red;">
|
59
|
+
<story/>
|
60
|
+
</div>
|
61
|
+
`,
|
62
|
+
}),
|
63
|
+
],
|
64
|
+
play: async ({ canvasElement, id }) => {
|
65
|
+
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
|
66
|
+
const canvas = within(canvasElement);
|
67
|
+
|
68
|
+
await channel.emit(RESET_STORY_ARGS, { storyId: id });
|
69
|
+
await new Promise((resolve) => {
|
70
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
71
|
+
});
|
72
|
+
const reactiveButton = await canvas.getByRole('button');
|
73
|
+
await expect(reactiveButton).toHaveTextContent('Wrapped Button 0');
|
74
|
+
|
75
|
+
await userEvent.click(reactiveButton); // click to update the label to increment the count + 1
|
76
|
+
await channel.emit(UPDATE_STORY_ARGS, {
|
77
|
+
storyId: id,
|
78
|
+
updatedArgs: { label: 'updated Wrapped Button' },
|
79
|
+
});
|
80
|
+
await new Promise((resolve) => {
|
81
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
82
|
+
});
|
83
|
+
await expect(canvas.getByRole('button')).toHaveTextContent('updated Wrapped Button 1');
|
84
|
+
|
85
|
+
await userEvent.click(reactiveButton); // click to update the label to increment the count + 1
|
86
|
+
await expect(reactiveButton).toHaveTextContent('updated Wrapped Button 2');
|
87
|
+
},
|
88
|
+
};
|
89
|
+
|
90
|
+
// to test that Simple html Decorators in CSF2 format are applied correctly in reactive mode
|
91
|
+
const ReactiveCSF2WrapperTempl: StoryFn = (args) => ({
|
92
|
+
components: { ReactiveArgs },
|
93
|
+
setup() {
|
94
|
+
return { args };
|
95
|
+
},
|
96
|
+
template: '<ReactiveArgs v-bind="args" />',
|
97
|
+
});
|
98
|
+
|
99
|
+
export const ReactiveCSF2Wrapper = ReactiveCSF2WrapperTempl.bind({});
|
100
|
+
|
101
|
+
ReactiveCSF2Wrapper.args = {
|
102
|
+
label: 'CSF2 Wrapped Button',
|
103
|
+
};
|
104
|
+
ReactiveCSF2Wrapper.decorators = [
|
105
|
+
() => ({
|
106
|
+
template: `
|
107
|
+
<div style="border: 5px solid red;">
|
108
|
+
<story/>
|
109
|
+
</div>
|
110
|
+
`,
|
111
|
+
}),
|
112
|
+
];
|
113
|
+
|
114
|
+
ReactiveCSF2Wrapper.play = async ({ canvasElement, id }) => {
|
115
|
+
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
|
116
|
+
const canvas = within(canvasElement);
|
117
|
+
|
118
|
+
await channel.emit(RESET_STORY_ARGS, { storyId: id });
|
119
|
+
await new Promise((resolve) => {
|
120
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
121
|
+
});
|
122
|
+
const reactiveButton = await canvas.getByRole('button');
|
123
|
+
await expect(reactiveButton).toHaveTextContent('CSF2 Wrapped Button 0');
|
124
|
+
|
125
|
+
await userEvent.click(reactiveButton); // click to update the label to increment the count + 1
|
126
|
+
await channel.emit(UPDATE_STORY_ARGS, {
|
127
|
+
storyId: id,
|
128
|
+
updatedArgs: { label: 'updated CSF2 Wrapped Button' },
|
129
|
+
});
|
130
|
+
await new Promise((resolve) => {
|
131
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
132
|
+
});
|
133
|
+
await expect(canvas.getByRole('button')).toHaveTextContent('updated CSF2 Wrapped Button 1');
|
134
|
+
|
135
|
+
await userEvent.click(reactiveButton); // click to update the label to increment the count + 1
|
136
|
+
await expect(reactiveButton).toHaveTextContent('updated CSF2 Wrapped Button 2');
|
137
|
+
};
|
@@ -0,0 +1,88 @@
|
|
1
|
+
import { global as globalThis } from '@storybook/global';
|
2
|
+
import { userEvent, within } from '@storybook/testing-library';
|
3
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
4
|
+
import { h } from 'vue';
|
5
|
+
import { RESET_STORY_ARGS, STORY_ARGS_UPDATED, UPDATE_STORY_ARGS } from '@storybook/core-events';
|
6
|
+
import Reactivity from './Reactivity.vue';
|
7
|
+
|
8
|
+
const meta = {
|
9
|
+
component: Reactivity,
|
10
|
+
argTypes: {
|
11
|
+
header: { control: { type: 'text' } },
|
12
|
+
footer: { control: { type: 'text' } },
|
13
|
+
default: { control: { type: 'text' } },
|
14
|
+
},
|
15
|
+
args: {
|
16
|
+
label: 'If you see this then the label arg was not reactive.',
|
17
|
+
default: 'If you see this then the default slot was not reactive.',
|
18
|
+
header: 'If you see this, the header slot was not reactive.', // this can be useless if you have custom render function that overrides the slot
|
19
|
+
footer: 'If you see this, the footer slot was not reactive.',
|
20
|
+
},
|
21
|
+
play: async ({ canvasElement, id, args }) => {
|
22
|
+
const channel = (globalThis as any).__STORYBOOK_ADDONS_CHANNEL__;
|
23
|
+
|
24
|
+
const canvas = within(canvasElement);
|
25
|
+
|
26
|
+
await channel.emit(RESET_STORY_ARGS, { storyId: id });
|
27
|
+
await new Promise((resolve) => channel.once(STORY_ARGS_UPDATED, resolve));
|
28
|
+
|
29
|
+
const input = await canvas.findByLabelText<HTMLInputElement>('Some input:');
|
30
|
+
await userEvent.type(input, 'value');
|
31
|
+
|
32
|
+
await channel.emit(UPDATE_STORY_ARGS, {
|
33
|
+
storyId: id,
|
34
|
+
updatedArgs: {
|
35
|
+
label: 'updated label',
|
36
|
+
header: 'updated header slot', // this can be useless if you have custom render function that overrides the slot which the case here
|
37
|
+
footer: 'updated footer slot',
|
38
|
+
default: 'updated default slot',
|
39
|
+
},
|
40
|
+
});
|
41
|
+
await new Promise((resolve) => channel.once(STORY_ARGS_UPDATED, resolve));
|
42
|
+
},
|
43
|
+
} satisfies Meta<typeof Reactivity>;
|
44
|
+
|
45
|
+
export default meta;
|
46
|
+
type Story = StoryObj<typeof meta>;
|
47
|
+
|
48
|
+
export const NoDecorators: Story = {};
|
49
|
+
|
50
|
+
export const DecoratorFunctionalComponent: Story = {
|
51
|
+
decorators: [
|
52
|
+
(storyFn, context) => {
|
53
|
+
const story = storyFn();
|
54
|
+
return () => h('div', [h('h2', ['Decorator not using args']), [h(story)]]);
|
55
|
+
},
|
56
|
+
],
|
57
|
+
};
|
58
|
+
|
59
|
+
export const DecoratorFunctionalComponentArgsFromContext: Story = {
|
60
|
+
decorators: [
|
61
|
+
(storyFn, context) => {
|
62
|
+
const story = storyFn();
|
63
|
+
return () =>
|
64
|
+
h('div', [h('h2', ['Decorator using args.label: ', context.args.label]), [h(story)]]);
|
65
|
+
},
|
66
|
+
],
|
67
|
+
};
|
68
|
+
|
69
|
+
export const DecoratorComponentOptions: Story = {
|
70
|
+
decorators: [
|
71
|
+
(storyFn, context) => {
|
72
|
+
return {
|
73
|
+
template: '<div><h2>Decorator not using args</h2><story/></div>',
|
74
|
+
};
|
75
|
+
},
|
76
|
+
],
|
77
|
+
};
|
78
|
+
|
79
|
+
export const DecoratorComponentOptionsArgsFromData: Story = {
|
80
|
+
decorators: [
|
81
|
+
(storyFn, context) => {
|
82
|
+
return {
|
83
|
+
data: () => ({ args: context.args }),
|
84
|
+
template: '<div><h2>Decorator using args.label: {{args.label}}</h2><story/></div>',
|
85
|
+
};
|
86
|
+
},
|
87
|
+
],
|
88
|
+
};
|
@@ -0,0 +1,127 @@
|
|
1
|
+
import { expect } from '@storybook/jest';
|
2
|
+
import { global as globalThis } from '@storybook/global';
|
3
|
+
import { within } from '@storybook/testing-library';
|
4
|
+
import { STORY_ARGS_UPDATED, RESET_STORY_ARGS, UPDATE_STORY_ARGS } from '@storybook/core-events';
|
5
|
+
import { h } from 'vue';
|
6
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
7
|
+
import BaseLayout from './BaseLayout.vue';
|
8
|
+
|
9
|
+
const meta = {
|
10
|
+
component: BaseLayout,
|
11
|
+
args: {
|
12
|
+
label: 'Storybook Day',
|
13
|
+
default: () => 'Default Text Slot',
|
14
|
+
footer: h('p', 'Footer VNode Slot'),
|
15
|
+
},
|
16
|
+
tags: ['autodocs'],
|
17
|
+
} satisfies Meta<typeof BaseLayout>;
|
18
|
+
|
19
|
+
export default meta;
|
20
|
+
type Story = StoryObj<typeof meta>;
|
21
|
+
|
22
|
+
export const SimpleSlotTest: Story = {
|
23
|
+
args: {
|
24
|
+
label: 'Storybook Day',
|
25
|
+
header: () => h('h1', 'Header Text Slot'),
|
26
|
+
default: () => 'Default Text Slot',
|
27
|
+
footer: h('p', 'Footer VNode Slot'),
|
28
|
+
},
|
29
|
+
play: async ({ canvasElement, id }) => {
|
30
|
+
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
|
31
|
+
const canvas = within(canvasElement);
|
32
|
+
|
33
|
+
await channel.emit(RESET_STORY_ARGS, { storyId: id });
|
34
|
+
await new Promise((resolve) => {
|
35
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
36
|
+
});
|
37
|
+
await expect(canvas.getByTestId('footer-slot').innerText).toContain('Footer VNode Slot');
|
38
|
+
await expect(canvas.getByTestId('default-slot').innerText).toContain('Default Text Slot');
|
39
|
+
|
40
|
+
await channel.emit(UPDATE_STORY_ARGS, {
|
41
|
+
storyId: id,
|
42
|
+
updatedArgs: {
|
43
|
+
default: () => 'Default Text Slot Updated',
|
44
|
+
footer: h('p', 'Footer VNode Slot Updated'),
|
45
|
+
},
|
46
|
+
});
|
47
|
+
await new Promise((resolve) => {
|
48
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
49
|
+
});
|
50
|
+
await expect(canvas.getByTestId('default-slot').innerText).toContain(
|
51
|
+
'Default Text Slot Updated'
|
52
|
+
);
|
53
|
+
await expect(canvas.getByTestId('footer-slot').innerText).toContain(
|
54
|
+
'Footer VNode Slot Updated'
|
55
|
+
);
|
56
|
+
},
|
57
|
+
};
|
58
|
+
|
59
|
+
export const NamedSlotTest: Story = {
|
60
|
+
args: {
|
61
|
+
label: 'Storybook Day',
|
62
|
+
header: ({ title }: { title: string }) => h('h1', title),
|
63
|
+
default: () => 'Default Text Slot',
|
64
|
+
footer: h('p', 'Footer VNode Slot'),
|
65
|
+
},
|
66
|
+
// test that args are updated correctly in rective mode
|
67
|
+
play: async ({ canvasElement, id }) => {
|
68
|
+
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
|
69
|
+
const canvas = within(canvasElement);
|
70
|
+
|
71
|
+
await channel.emit(RESET_STORY_ARGS, { storyId: id });
|
72
|
+
await new Promise((resolve) => {
|
73
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
74
|
+
});
|
75
|
+
await expect(canvas.getByTestId('header-slot').innerText).toContain(
|
76
|
+
'Header title from the slot'
|
77
|
+
);
|
78
|
+
await expect(canvas.getByTestId('default-slot').innerText).toContain('Default Text Slot');
|
79
|
+
await expect(canvas.getByTestId('footer-slot').innerText).toContain('Footer VNode Slot');
|
80
|
+
|
81
|
+
await channel.emit(UPDATE_STORY_ARGS, {
|
82
|
+
storyId: id,
|
83
|
+
updatedArgs: {
|
84
|
+
default: () => 'Default Text Slot Updated',
|
85
|
+
footer: h('p', 'Footer VNode Slot Updated'),
|
86
|
+
},
|
87
|
+
});
|
88
|
+
await new Promise((resolve) => {
|
89
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
90
|
+
});
|
91
|
+
await expect(canvas.getByTestId('default-slot').innerText).toContain(
|
92
|
+
'Default Text Slot Updated'
|
93
|
+
);
|
94
|
+
await expect(canvas.getByTestId('footer-slot').innerText).toContain(
|
95
|
+
'Footer VNode Slot Updated'
|
96
|
+
);
|
97
|
+
},
|
98
|
+
};
|
99
|
+
|
100
|
+
export const SlotWithRenderFn: Story = {
|
101
|
+
args: {
|
102
|
+
label: 'Storybook Day',
|
103
|
+
header: ({ title }: { title: string }) => `${title}`,
|
104
|
+
default: () => 'Default Text Slot',
|
105
|
+
footer: h('p', 'Footer VNode Slot'),
|
106
|
+
},
|
107
|
+
render: (args) => ({
|
108
|
+
components: { BaseLayout },
|
109
|
+
setup() {
|
110
|
+
return { args };
|
111
|
+
},
|
112
|
+
template: `<BaseLayout :label="args.label" data-testid="layout">
|
113
|
+
{{args.default()}}
|
114
|
+
<template #header="{ title }"><h1>{{args.header({title})}}</h1></template>
|
115
|
+
</BaseLayout>`,
|
116
|
+
}),
|
117
|
+
play: async ({ canvasElement, id }) => {
|
118
|
+
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
|
119
|
+
const canvas = within(canvasElement);
|
120
|
+
|
121
|
+
await channel.emit(RESET_STORY_ARGS, { storyId: id });
|
122
|
+
await new Promise((resolve) => {
|
123
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
124
|
+
});
|
125
|
+
await expect(canvas.getByTestId('layout').innerText).toContain('Default Text Slot');
|
126
|
+
},
|
127
|
+
};
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<script setup lang="ts">
|
2
|
+
defineProps<{ label: string }>();
|
3
|
+
</script>
|
4
|
+
<template>
|
5
|
+
<div style="padding: 20px;background-color: pink;">
|
6
|
+
<header data-testid="header-slot">
|
7
|
+
<slot name="header" title="Header title from the slot">
|
8
|
+
If you see this, the header slot was not reactive.
|
9
|
+
</slot>
|
10
|
+
</header>
|
11
|
+
<div id="content">
|
12
|
+
<label>
|
13
|
+
Some input:
|
14
|
+
<input style='width: 400px' placeholder='If you see this, an args update caused the input field to loose state' />
|
15
|
+
</label>
|
16
|
+
<hr>
|
17
|
+
<button class="storybook-button storybook-button--primary storybook-button--medium"> {{ label
|
18
|
+
}}</button>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<main data-testid="default-slot">
|
22
|
+
<slot>Default slot placeholder</slot>
|
23
|
+
</main>
|
24
|
+
<footer data-testid="footer-slot">
|
25
|
+
<slot name="footer">
|
26
|
+
Footer slot placeholder
|
27
|
+
</slot>
|
28
|
+
</footer>
|
29
|
+
</div>
|
30
|
+
</template>
|
31
|
+
|
32
|
+
<style>
|
33
|
+
header,
|
34
|
+
footer {
|
35
|
+
background-color: #fff0ff;
|
36
|
+
padding: 20px;
|
37
|
+
}
|
38
|
+
|
39
|
+
main,
|
40
|
+
#content {
|
41
|
+
background-color: #f0f0f0;
|
42
|
+
padding: 20px;
|
43
|
+
}
|
44
|
+
</style>
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import { expect } from '@storybook/jest';
|
2
|
+
import { global as globalThis } from '@storybook/global';
|
3
|
+
import type { Channel } from '@storybook/channels';
|
4
|
+
import { within } from '@storybook/testing-library';
|
5
|
+
import { UPDATE_STORY_ARGS, STORY_ARGS_UPDATED, RESET_STORY_ARGS } from '@storybook/core-events';
|
6
|
+
|
7
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
8
|
+
import MySlotComponent from './MySlotComponent.vue';
|
9
|
+
|
10
|
+
declare global {
|
11
|
+
// eslint-disable-next-line no-var,vars-on-top,@typescript-eslint/naming-convention
|
12
|
+
var __STORYBOOK_ADDONS_CHANNEL__: Channel;
|
13
|
+
}
|
14
|
+
|
15
|
+
const meta = {
|
16
|
+
component: MySlotComponent,
|
17
|
+
args: {
|
18
|
+
label: 'Storybook Day',
|
19
|
+
year: 2022,
|
20
|
+
default: ({ text, year }) => `${text}, ${year}`,
|
21
|
+
},
|
22
|
+
tags: ['autodocs'],
|
23
|
+
} satisfies Meta<typeof MySlotComponent>;
|
24
|
+
|
25
|
+
export default meta;
|
26
|
+
type Story = StoryObj<typeof meta>;
|
27
|
+
|
28
|
+
export const Basic: Story = {
|
29
|
+
// test that args are updated correctly in reactive mode
|
30
|
+
play: async ({ canvasElement, id }) => {
|
31
|
+
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
|
32
|
+
const canvas = within(canvasElement);
|
33
|
+
|
34
|
+
await channel.emit(RESET_STORY_ARGS, { storyId: id });
|
35
|
+
await new Promise((resolve) => channel.once(STORY_ARGS_UPDATED, resolve));
|
36
|
+
await expect(canvas.getByTestId('scoped-slot').innerText).toMatch(
|
37
|
+
'Hello Storybook Day from the slot, 2022'
|
38
|
+
);
|
39
|
+
|
40
|
+
await channel.emit(UPDATE_STORY_ARGS, {
|
41
|
+
storyId: id,
|
42
|
+
updatedArgs: {
|
43
|
+
label: 'Storybook Day updated',
|
44
|
+
year: 2023,
|
45
|
+
},
|
46
|
+
});
|
47
|
+
await new Promise((resolve) => {
|
48
|
+
channel.once(STORY_ARGS_UPDATED, resolve);
|
49
|
+
});
|
50
|
+
|
51
|
+
await expect(canvas.getByTestId('scoped-slot').innerText).toMatch(
|
52
|
+
'Hello Storybook Day updated from the slot, 2023'
|
53
|
+
);
|
54
|
+
},
|
55
|
+
};
|
56
|
+
|
57
|
+
export const CustomRender: Story = {
|
58
|
+
render: (args) => ({
|
59
|
+
components: { MySlotComponent },
|
60
|
+
setup() {
|
61
|
+
return { args };
|
62
|
+
},
|
63
|
+
template: `<MySlotComponent v-bind="args" v-slot="slotProps">
|
64
|
+
{{ slotProps.text }}, {{ slotProps.year }}
|
65
|
+
</MySlotComponent>`,
|
66
|
+
}),
|
67
|
+
play: Basic.play,
|
68
|
+
};
|
69
|
+
|
70
|
+
export const CustomRenderUsingFunctionSlot: Story = {
|
71
|
+
render: (args: any) => ({
|
72
|
+
components: { MySlotComponent },
|
73
|
+
setup() {
|
74
|
+
return { args };
|
75
|
+
},
|
76
|
+
template: `<MySlotComponent v-bind="args" v-slot="slotProps">
|
77
|
+
{{args.default(slotProps)}}
|
78
|
+
</MySlotComponent>`,
|
79
|
+
}),
|
80
|
+
play: Basic.play,
|
81
|
+
};
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
2
|
+
|
3
|
+
import GlobalUsage from './GlobalUsage.vue';
|
4
|
+
import GlobalSetup from './GlobalSetup.vue';
|
5
|
+
|
6
|
+
const meta: Meta = {
|
7
|
+
component: GlobalUsage,
|
8
|
+
argTypes: {},
|
9
|
+
tags: ['autodocs'],
|
10
|
+
} satisfies Meta<typeof GlobalUsage>;
|
11
|
+
|
12
|
+
export default meta;
|
13
|
+
type Story = StoryObj<typeof meta>;
|
14
|
+
|
15
|
+
export const MultiComponents: Story = {
|
16
|
+
args: {
|
17
|
+
label: 'Button',
|
18
|
+
size: 'large',
|
19
|
+
backgroundColor: '#aa00ff',
|
20
|
+
btn1Args: { label: 'Button 10', size: 'small', backgroundColor: '#aa00ff' },
|
21
|
+
},
|
22
|
+
render(args: any) {
|
23
|
+
return {
|
24
|
+
components: { GlobalUsage, GlobalSetup },
|
25
|
+
setup() {
|
26
|
+
return { args };
|
27
|
+
},
|
28
|
+
template: `<div style="background-color:pink;opacity:0.9;padding:20px" >
|
29
|
+
|
30
|
+
<div style="display:flex;gap:10px">
|
31
|
+
<img src="https://user-images.githubusercontent.com/263385/199832481-bbbf5961-6a26-481d-8224-51258cce9b33.png" width="200" />
|
32
|
+
<GlobalUsage v-bind="args.btn1Args" />
|
33
|
+
</div>
|
34
|
+
<h2>Complex Story Custom template </h2> <br/> <hr/>
|
35
|
+
|
36
|
+
<GlobalSetup :label="args.label" />
|
37
|
+
<div style="margin:8px"><span style="font-size:28px;color:green">Multiple </span>
|
38
|
+
<span style="background-color:magenta;opacity:0.9;padding:8px"><i>Components</i></span></div>
|
39
|
+
<div style="display:flex;gap:10px">
|
40
|
+
<GlobalUsage v-bind="args" />
|
41
|
+
<GlobalUsage label="Static Label Dynamic color" :background-color="args.backgroundColor"/>
|
42
|
+
</div>
|
43
|
+
</div>`,
|
44
|
+
};
|
45
|
+
},
|
46
|
+
};
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import type { DecoratorFunction } from '@storybook/csf';
|
2
|
+
import { global as globalThis } from '@storybook/global';
|
3
|
+
import type { Meta, StoryObj, VueRenderer } from '@storybook/vue3';
|
4
|
+
import { h } from 'vue';
|
5
|
+
|
6
|
+
const { Button, Pre } = (globalThis as any).Components;
|
7
|
+
|
8
|
+
const meta = {
|
9
|
+
component: Button,
|
10
|
+
} satisfies Meta<typeof Button>;
|
11
|
+
|
12
|
+
export default meta;
|
13
|
+
|
14
|
+
type Story = StoryObj<typeof meta>;
|
15
|
+
|
16
|
+
const ComponentTemplateWrapper = () => ({
|
17
|
+
components: {
|
18
|
+
Pre,
|
19
|
+
},
|
20
|
+
template: `
|
21
|
+
<Pre text="decorator" />
|
22
|
+
<story v-bind="$attrs"/>
|
23
|
+
`,
|
24
|
+
});
|
25
|
+
|
26
|
+
const SimpleTemplateWrapper = () => ({
|
27
|
+
template: `
|
28
|
+
<div style="border: 5px solid red;">
|
29
|
+
<story/>
|
30
|
+
</div>
|
31
|
+
`,
|
32
|
+
});
|
33
|
+
|
34
|
+
const VueWrapperWrapper: DecoratorFunction<VueRenderer> = (storyFn, context) => {
|
35
|
+
// Call the `storyFn` to receive a component that Vue can render
|
36
|
+
const story = storyFn();
|
37
|
+
// Vue 3 "Functional" component as decorator
|
38
|
+
return () => {
|
39
|
+
return h('div', { style: 'border: 5px solid blue' }, h(story, context.args));
|
40
|
+
};
|
41
|
+
};
|
42
|
+
|
43
|
+
const DynamicWrapperWrapper: DecoratorFunction<VueRenderer> = (storyFn, { args }) => ({
|
44
|
+
template: `<div :style="{ borderWidth: level, borderColor: 'green', borderStyle: 'solid' }"><story /></div>`,
|
45
|
+
computed: { level: () => `${args.level}px` },
|
46
|
+
});
|
47
|
+
|
48
|
+
export const ComponentTemplate: Story = {
|
49
|
+
args: { label: 'With component' },
|
50
|
+
decorators: [ComponentTemplateWrapper],
|
51
|
+
};
|
52
|
+
|
53
|
+
export const SimpleTemplate: Story = {
|
54
|
+
args: { label: 'With border' },
|
55
|
+
decorators: [SimpleTemplateWrapper],
|
56
|
+
};
|
57
|
+
|
58
|
+
export const VueWrapper: Story = {
|
59
|
+
args: { label: 'With Vue wrapper' },
|
60
|
+
decorators: [VueWrapperWrapper],
|
61
|
+
};
|
62
|
+
|
63
|
+
export const DynamicWrapper: Story = {
|
64
|
+
args: { label: 'With dynamic wrapper', primary: true },
|
65
|
+
argTypes: {
|
66
|
+
// Number type is detected, but we still want to constrain the range from 1-6
|
67
|
+
level: { control: { type: 'range', min: 1, max: 6 } },
|
68
|
+
},
|
69
|
+
decorators: [DynamicWrapperWrapper],
|
70
|
+
};
|
71
|
+
|
72
|
+
export const MultipleWrappers = {
|
73
|
+
args: { label: 'With multiple wrappers' },
|
74
|
+
argTypes: {
|
75
|
+
// Number type is detected, but we still want to constrain the range from 1-6
|
76
|
+
level: { control: { type: 'range', min: 1, max: 6 } },
|
77
|
+
},
|
78
|
+
decorators: [
|
79
|
+
ComponentTemplateWrapper,
|
80
|
+
SimpleTemplateWrapper,
|
81
|
+
VueWrapperWrapper,
|
82
|
+
DynamicWrapperWrapper,
|
83
|
+
],
|
84
|
+
};
|
package/dist/chunk-2GDW2BFM.mjs
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf,__hasOwnProp=Object.prototype.hasOwnProperty;var __commonJS=(cb,mod)=>function(){return mod||(0,cb[__getOwnPropNames(cb)[0]])((mod={exports:{}}).exports,mod),mod.exports};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:!0}):target,mod));import{h}from"vue";import{sanitizeStoryContextUpdate}from"@storybook/preview-api";function normalizeFunctionalComponent(options){return typeof options=="function"?{render:options,name:options.name}:options}function prepare(rawStory,innerStory){let story=rawStory;return story==null?null:innerStory?{...normalizeFunctionalComponent(story),components:{...story.components||{},story:innerStory}}:{render(){return h(story)}}}function decorateStory(storyFn,decorators){return decorators.reduce((decorated,decorator)=>context=>{let story,decoratedStory=decorator(update=>(story=decorated({...context,...sanitizeStoryContextUpdate(update)}),story),context);return story||(story=decorated(context)),decoratedStory===story?story:prepare(decoratedStory,h(story,context.args))},context=>prepare(storyFn(context)))}import{createApp,h as h2,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 h2(Component,props,getSlots(props,context))},setupFunction=_app=>{},setup=fn=>{setupFunction=fn},map=new Map,elementMap=new Map;function renderToCanvas({storyFn,forceRemount,showMain,showException,storyContext},canvasElement){storyContext.args=reactive(storyContext.args);let element=storyFn();elementMap.set(canvasElement,element);let props=element.render?.().props,reactiveArgs=props?reactive(props):storyContext.args,existingApp=map.get(canvasElement);if(existingApp&&!forceRemount)return updateArgs(existingApp.reactiveArgs,reactiveArgs),()=>{teardown(existingApp.vueApp,canvasElement)};existingApp&&forceRemount&&teardown(existingApp.vueApp,canvasElement);let storybookApp=createApp({render(){let renderedElement=elementMap.get(canvasElement),current=renderedElement&&renderedElement.template?renderedElement:element;return map.set(canvasElement,{vueApp:storybookApp,reactiveArgs}),h2(current,reactiveArgs)}});return storybookApp.config.errorHandler=e=>showException(e),setupFunction(storybookApp),storybookApp.mount(canvasElement),showMain(),()=>{teardown(storybookApp,canvasElement)}}function getSlots(props,context){let{argTypes}=context,slots=Object.entries(props).filter(([key,value])=>argTypes[key]?.table?.category==="slots").map(([key,value])=>[key,typeof value=="function"?value:()=>value]);return Object.fromEntries(slots)}function updateArgs(reactiveArgs,nextArgs){nextArgs&&(Object.keys(reactiveArgs).forEach(key=>{delete reactiveArgs[key]}),Object.assign(reactiveArgs,nextArgs))}function teardown(storybookApp,canvasElement){storybookApp?.unmount(),map.has(canvasElement)&&map.delete(canvasElement)}export{__commonJS,__toESM,decorateStory,render,setup,renderToCanvas};
|