Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue3:fix reactive decorators #21954

Merged
merged 153 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from 145 commits
Commits
Show all changes
153 commits
Select commit Hold shift + click to select a range
9f1f4ef
rewrite source decorator with optimisation
chakAs3 Feb 23, 2023
c416f70
some experimental
chakAs3 Feb 23, 2023
8ef5ad5
Merge branch 'next-remote' into chaks/vue3-source-fix
chakAs3 Feb 23, 2023
3dc96b1
start writing propre tests for source decorator
chakAs3 Feb 24, 2023
207ab63
write tests for 2 main src decorator functions
chakAs3 Feb 24, 2023
6ab9fc1
add new tests
chakAs3 Feb 24, 2023
46307ba
adjusting the render with tests
chakAs3 Feb 25, 2023
4c14b10
add more tests for sources decorator
chakAs3 Feb 25, 2023
c8b7e68
testing the render
chakAs3 Feb 25, 2023
c8e157e
vue3 fixing prepareStory for decotators
chakAs3 Feb 27, 2023
2a81e98
some refactoring and cleanup
chakAs3 Feb 27, 2023
dab4855
after merging next branch
chakAs3 Feb 27, 2023
233361a
fix null slots
chakAs3 Feb 27, 2023
2b205f8
handle no slots , null issue
chakAs3 Feb 27, 2023
b874cbe
fix args Inheritance and reactivity
chakAs3 Mar 1, 2023
d3f061a
merging next branch
chakAs3 Mar 1, 2023
3d0b620
fix null value exception for mdx stories
chakAs3 Mar 1, 2023
da8b3b4
fix slot double quote display
chakAs3 Mar 1, 2023
dc32a83
fix source for mdx using originFn and context
chakAs3 Mar 1, 2023
14eca7c
refactory generateSource to be elegante
chakAs3 Mar 2, 2023
b758496
add some tests for source decorator and render
chakAs3 Mar 2, 2023
265fc65
merging next to local branch
chakAs3 Mar 3, 2023
ada469d
add some tests and fix CSF2 with decorator
chakAs3 Mar 4, 2023
284313a
fixing react decorator breaks the reactivity
chakAs3 Mar 4, 2023
a65844e
complete merging next branch
chakAs3 Mar 4, 2023
4a7d98b
use remount as easier way
chakAs3 Mar 4, 2023
f681d83
emit using channel forceRemount
chakAs3 Mar 4, 2023
f670ef5
cleanup code
chakAs3 Mar 4, 2023
a5284cd
fix args after tests + cleanup
chakAs3 Mar 4, 2023
fb1c507
attribute style dynamic source
chakAs3 Mar 5, 2023
c0ffbe0
string double quote isse
chakAs3 Mar 5, 2023
7183154
v-bind , style display as string
chakAs3 Mar 6, 2023
db908d3
style should be dynamic in source
chakAs3 Mar 6, 2023
6d4d3aa
cleanup after testing.
chakAs3 Mar 6, 2023
13699fe
refactor add utils to docs
chakAs3 Mar 6, 2023
70da242
merging next branch
chakAs3 Mar 6, 2023
311a69e
add utils
chakAs3 Mar 6, 2023
d38c8ac
use vue watch() instead of useEffect()
chakAs3 Mar 7, 2023
13af5c9
merge next branch
chakAs3 Mar 7, 2023
51cf252
just to rerun the ci, it did pass on server y??
chakAs3 Mar 7, 2023
20bee15
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 7, 2023
6c806b6
fix slots reactivity without render function temp
chakAs3 Mar 8, 2023
a75e222
fix undefined attr + cleanup
chakAs3 Mar 8, 2023
3919b51
merge next branch
chakAs3 Mar 9, 2023
fc833f4
run decorators in renderToCanvas save reactivity
chakAs3 Mar 9, 2023
a46d316
great reasult finally
chakAs3 Mar 9, 2023
3e20291
merge next branch
chakAs3 Mar 9, 2023
069d615
add try catch in case decorator are react Fn use
chakAs3 Mar 9, 2023
8a3ea0a
slots properly rendered , and source generated
chakAs3 Mar 10, 2023
4408459
cleanup
chakAs3 Mar 10, 2023
4b3ee67
source generating for slots with recusivity with
chakAs3 Mar 10, 2023
d8c87af
refactory and cleanup
chakAs3 Mar 13, 2023
1aa308a
merge next branch
chakAs3 Mar 13, 2023
5643df2
fix the units tests
chakAs3 Mar 13, 2023
4634f30
remove console logs
chakAs3 Mar 13, 2023
12dd5fb
cleanup oode and try to solve yn pkg issue
chakAs3 Mar 13, 2023
f2a8cc7
cleanup and refactory
chakAs3 Mar 13, 2023
42efaff
Merge remote-tracking branch 'origin/next' into chaks/vue3-source-fix
chakAs3 Mar 13, 2023
9c8ae2c
fix prep, pass tests
chakAs3 Mar 13, 2023
6e10751
Merge remote-tracking branch 'origin/next' into chaks/vue3-source-fix
chakAs3 Mar 13, 2023
3add1df
refactory get slots
chakAs3 Mar 13, 2023
f274f05
fix args updates and correct unit tests
chakAs3 Mar 13, 2023
24aa492
fix args update
chakAs3 Mar 13, 2023
ad2963b
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 13, 2023
d62fa0f
fix hooks story tests, by triggering render all tree
chakAs3 Mar 14, 2023
29d0d8e
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 14, 2023
69ac813
add slots test story to vue3 renderer templates
chakAs3 Mar 14, 2023
1f863d2
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 14, 2023
4dec2e0
using storyFn to update args from decorators
chakAs3 Mar 15, 2023
b69c897
get it done properly yeeeey
chakAs3 Mar 15, 2023
88e654a
refactory after testings
chakAs3 Mar 15, 2023
228458b
fix tests
chakAs3 Mar 15, 2023
85d74d2
add scoped slot tests stories
chakAs3 Mar 15, 2023
647820d
added arg to Reactive SLot test story
chakAs3 Mar 15, 2023
c09528a
add test story without render fn for Scoped slots
chakAs3 Mar 16, 2023
76840f5
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 16, 2023
acd6563
Merge branch 'next' into chaksfi/vue3-source-fix
chakAs3 Mar 16, 2023
49a2fb5
get back eval for printing the object value
chakAs3 Mar 16, 2023
0411ab3
i don't know this passed the tests and now does not, some on reat:che…
chakAs3 Mar 16, 2023
08d1204
remove files
chakAs3 Mar 16, 2023
95f298d
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 16, 2023
4aad462
Improve ScopedSlot test and TS test coverage
kasperpeulen Mar 16, 2023
e6d2825
Add caching middleware back
kasperpeulen Mar 16, 2023
661b738
slots test story +Test reactivity with decorator and CSF2
chakAs3 Mar 20, 2023
564463f
cleaup logs
chakAs3 Mar 20, 2023
e6ade2c
add utility function
chakAs3 Mar 20, 2023
f4390e2
Merge branch 'chaks/vue3-source-fix' of https://github.com/storybookj…
chakAs3 Mar 20, 2023
bc9fe08
add Slot Story with Render to test interpolation & fixing unit test
chakAs3 Mar 20, 2023
423f86e
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 20, 2023
786b17c
fix variable declaration source
chakAs3 Mar 20, 2023
67b4ea6
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 20, 2023
35a3638
fix unit tests
chakAs3 Mar 20, 2023
bad517f
remove logs..
chakAs3 Mar 20, 2023
1188c57
fix TS issue , check not passing
chakAs3 Mar 20, 2023
cde13df
Merge branch 'next' into chaks/vue3-soe-fix
chakAs3 Mar 21, 2023
fdc1749
fix ci add vite 4.2.0 to resolutions sandboxes
chakAs3 Mar 21, 2023
9d98535
Merge branch 'next' into chaks/vue3-source-fix
chakAs3 Mar 22, 2023
1dec3ea
Start with tests
kasperpeulen Mar 27, 2023
1557a74
Merge branch 'next' into chaks/vue3-source-fix
dxb-story Mar 29, 2023
049df9d
Merge branch 'next' into chaks/vue3-source-fix
dxb-story Mar 30, 2023
65a1964
remove the watch on context.globals, we call anyway storyFn on render…
dxb-story Mar 31, 2023
960952c
merge next chaks/vue3-source-fix
dxb-story Mar 31, 2023
eab5643
merge next intot chaks/vue3-source-fix
chakAs3 Apr 3, 2023
ec774f8
merge next branch
chakAs3 Apr 5, 2023
ea0157d
revert source decorator to next branch
chakAs3 Apr 5, 2023
5f25b9a
passe context.args to keep the args reactive
chakAs3 Apr 5, 2023
c638d2b
remove utils keep next branch code
chakAs3 Apr 5, 2023
fdd85d9
merge next
chakAs3 Apr 5, 2023
aef38c1
Merge branch 'next' into chaks/vue3-reactive-decorators
chakAs3 Apr 5, 2023
4c5bc51
remove comments from render.ts
chakAs3 Apr 5, 2023
251c006
Merge branch 'next' into kasper/vue-tests
chakAs3 Apr 7, 2023
d974fcf
Merge branch 'chaks/vue3-reactive-decorators' into kasper/vue-tests-w…
chakAs3 Apr 10, 2023
2847022
rework test stories visually & functionally to match vue reactivty/r…
chakAs3 Apr 10, 2023
df03af5
return the functional component instead normalize it with Render fun…
chakAs3 Apr 10, 2023
ec787ca
Merge branch 'next' into chaks/vue3-reactive-decorators
chakAs3 Apr 10, 2023
0e7b798
Merge branch 'chaks/vue3-reactive-decorators' into kasper/vue-tests-w…
chakAs3 Apr 10, 2023
8862c83
Merge branch 'next' into chaks/vue3-reactive-decorators
chakAs3 Apr 10, 2023
1c2a73b
remove unecessary files & rename the stories files
chakAs3 Apr 10, 2023
cd7cba6
passing context args to inner story
chakAs3 Apr 10, 2023
2c4849d
use the usual test api ( getByRole ..)
chakAs3 Apr 10, 2023
e0f310a
remove console.log
chakAs3 Apr 10, 2023
0dc3299
cleanup code
chakAs3 Apr 12, 2023
3f62d1e
revise the tests + support component props declaration
chakAs3 Apr 12, 2023
dc888ab
add emit reset & change args to simulate the changes
chakAs3 Apr 13, 2023
4a6ace0
cleanup and add more comments to make it clear
chakAs3 Apr 13, 2023
ab19689
Merge branch 'next' into chaks/vue3-reactive-decorators
chakAs3 Apr 13, 2023
7208a63
fix type:check
chakAs3 Apr 13, 2023
ab6738f
cleaup comments and remove no used calls
chakAs3 Apr 13, 2023
a0a47f8
fixing ts check
chakAs3 Apr 13, 2023
6df145a
removing some experiments
chakAs3 Apr 14, 2023
95ff25b
fixing reactive with using functional component as render
chakAs3 Apr 14, 2023
da27a5b
add test for reactive functional component render
chakAs3 Apr 14, 2023
186b10c
refactory and cleanup story preparation
chakAs3 Apr 17, 2023
19be3b2
Merge remote-tracking branch 'origin/next' into chaks/vue3-reactive-d…
chakAs3 Apr 17, 2023
6624221
fix linter cercleci
chakAs3 Apr 17, 2023
036e28a
Update tests
kasperpeulen Apr 17, 2023
ab083db
Merge remote-tracking branch 'origin/chaks/vue3-reactive-decorators' …
kasperpeulen Apr 17, 2023
de26d1e
Apply args to functional decorators.
kasperpeulen Apr 17, 2023
01aee9c
Rename
kasperpeulen Apr 17, 2023
9e974b4
Add reactive slot test
kasperpeulen Apr 17, 2023
0fc05ca
Apply args to functional decorators.
chakAs3 Apr 18, 2023
5edbd29
Merge branch 'chaks/vue3-reactive-decorators' of https://github.com/s…
chakAs3 Apr 18, 2023
620f87b
cleanup test logs
chakAs3 Apr 18, 2023
60968cd
rectify the visual tests to have proper testing
chakAs3 Apr 18, 2023
9f1c700
rectify visual tests to perform proper tests
chakAs3 Apr 18, 2023
11926f6
Merge branch 'next' into chaks/vue3-reactive-decorators
chakAs3 Apr 18, 2023
b41e300
add support reactive slots with default render function
chakAs3 Apr 18, 2023
6c24346
complete the slots test stories
chakAs3 Apr 18, 2023
20ab58c
compte unit tests for reactive slots all types
chakAs3 Apr 18, 2023
667e494
refactory and cleanup
chakAs3 Apr 18, 2023
92e7a3b
fix default token error
chakAs3 Apr 18, 2023
aea5936
normalize unit test / visual test stories
chakAs3 Apr 18, 2023
d3e3522
fix arg type in slot function
chakAs3 Apr 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions code/lib/store/template/stories/args.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ export const Events = {
parameters: { argNames: ['test'] },
play: async ({ canvasElement, id }: PlayFunctionContext<any>) => {
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
await channel.emit(RESET_STORY_ARGS, { storyId: id });
await new Promise((resolve) => channel.once(STORY_ARGS_UPDATED, resolve));

await within(canvasElement).findByText(/initial/);

await channel.emit(UPDATE_STORY_ARGS, { storyId: id, updatedArgs: { test: 'updated' } });
await within(canvasElement).findByText(/updated/);

await channel.emit(RESET_STORY_ARGS, { storyId: id });
await new Promise((resolve) => channel.once(STORY_ARGS_UPDATED, resolve));
await within(canvasElement).findByText(/initial/);
await within(canvasElement).findByText(/updated/);
},
};
1 change: 1 addition & 0 deletions code/lib/store/template/stories/globals.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const Events = {
],
play: async ({ canvasElement }: PlayFunctionContext<any>) => {
const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__;
await channel.emit('updateGlobals', { globals: { foo: 'fooValue' } });
await within(canvasElement).findByText('fooValue');

await channel.emit('updateGlobals', { globals: { foo: 'updated' } });
Expand Down
23 changes: 14 additions & 9 deletions code/renderers/vue3/src/decorateStory.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { ConcreteComponent, Component, ComponentOptions } from 'vue';
import { h } from 'vue';
import type { DecoratorFunction, StoryContext, LegacyStoryFn } from '@storybook/types';
import type { DecoratorFunction, StoryContext, LegacyStoryFn, Args } from '@storybook/types';
import { sanitizeStoryContextUpdate } from '@storybook/preview-api';

// eslint-disable-next-line import/no-extraneous-dependencies
import { looseEqual } from '@vue/shared';
import type { VueRenderer } from './types';

/*
Expand All @@ -11,6 +12,7 @@ import type { VueRenderer } from './types';
The concept is taken from Vue 3's `defineComponent` but changed from creating a `setup`
method on the ComponentOptions so end-users don't need to specify a "thunk" as a decorator.
*/

function normalizeFunctionalComponent(options: ConcreteComponent): ComponentOptions {
return typeof options === 'function' ? { render: options, name: options.name } : options;
}
Expand All @@ -20,11 +22,10 @@ function prepare(
innerStory?: ConcreteComponent
): Component | null {
const story = rawStory as ComponentOptions;

if (story == null) {
if (story === null) {
return null;
}

if (typeof story === 'function') return story; // we don't need to wrap a functional component nor to convert it to a component options
if (innerStory) {
return {
// Normalize so we can always spread an object
Expand All @@ -44,6 +45,7 @@ export function decorateStory(
storyFn: LegacyStoryFn<VueRenderer>,
decorators: DecoratorFunction<VueRenderer>[]
): LegacyStoryFn<VueRenderer> {
let updatedArgs: Args;
return decorators.reduce(
(decorated: LegacyStoryFn<VueRenderer>, decorator) => (context: StoryContext<VueRenderer>) => {
let story: VueRenderer['storyResult'] | undefined;
Expand All @@ -53,18 +55,21 @@ export function decorateStory(
...context,
...sanitizeStoryContextUpdate(update),
});

if (update && update.args && !looseEqual(update.args, context.args))
updatedArgs ??= update.args;
return story;
}, context);

if (!story) {
story = decorated(context);
}
context.args = updatedArgs ?? context.args;
if (!story) story = decorated(context);

if (decoratedStory === story) {
return story;
}

return prepare(decoratedStory, h(story, context.args)) as VueRenderer['storyResult'];
const innerStory = () => h(story!, context.args);
return prepare(decoratedStory, innerStory) as VueRenderer['storyResult'];
},
(context) => prepare(storyFn(context)) as LegacyStoryFn<VueRenderer>
);
Expand Down
89 changes: 89 additions & 0 deletions code/renderers/vue3/src/render.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { expectTypeOf } from 'expect-type';

import { reactive } from 'vue';
import { updateArgs } from './render';

describe('Render Story', () => {
test('update reactive Args updateArgs()', () => {
const reactiveArgs = reactive({ argFoo: 'foo', argBar: 'bar' }); // get reference to reactiveArgs or create a new one;
expectTypeOf(reactiveArgs).toMatchTypeOf<Record<string, any>>();
expectTypeOf(reactiveArgs).toEqualTypeOf<{ argFoo: string; argBar: string }>();

const newArgs = { argFoo: 'foo2', argBar: 'bar2' };
updateArgs(reactiveArgs, newArgs);
expectTypeOf(reactiveArgs).toEqualTypeOf<{ argFoo: string; argBar: string }>();
expect(reactiveArgs).toEqual({ argFoo: 'foo2', argBar: 'bar2' });
});

test('update reactive Args component inherit objectArg updateArgs()', () => {
const reactiveArgs = reactive({ objectArg: { argFoo: 'foo', argBar: 'bar' } }); // get reference to reactiveArgs or create a new one;
expectTypeOf(reactiveArgs).toMatchTypeOf<Record<string, any>>();
expectTypeOf(reactiveArgs).toEqualTypeOf<{ objectArg: { argFoo: string; argBar: string } }>();

const newArgs = { argFoo: 'foo2', argBar: 'bar2' };
updateArgs(reactiveArgs, newArgs);
expectTypeOf(reactiveArgs).toEqualTypeOf<{ objectArg: { argFoo: string; argBar: string } }>();
expect(reactiveArgs).toEqual({
argFoo: 'foo2',
argBar: 'bar2',
});
});

test('update reactive Args component inherit objectArg', () => {
const reactiveArgs = reactive({ objectArg: { argFoo: 'foo' } }); // get reference to reactiveArgs or create a new one;
expectTypeOf(reactiveArgs).toMatchTypeOf<Record<string, any>>();
expectTypeOf(reactiveArgs).toEqualTypeOf<{ objectArg: { argFoo: string } }>();

const newArgs = { argFoo: 'foo2', argBar: 'bar2' };
updateArgs(reactiveArgs, newArgs);
expect(reactiveArgs).toEqual({ argFoo: 'foo2', argBar: 'bar2' });
});

test('update reactive Args component 2 object args -> updateArgs()', () => {
const reactiveArgs = reactive({
objectArg: { argFoo: 'foo' },
objectArg2: { argBar: 'bar' },
}); // get reference to reactiveArgs or create a new one;
expectTypeOf(reactiveArgs).toMatchTypeOf<Record<string, any>>();
expectTypeOf(reactiveArgs).toEqualTypeOf<{
objectArg: { argFoo: string };
objectArg2: { argBar: string };
}>();

const newArgs = { argFoo: 'foo2', argBar: 'bar2' };
updateArgs(reactiveArgs, newArgs);

expect(reactiveArgs).toEqual({
argFoo: 'foo2',
argBar: 'bar2',
});
});

test('update reactive Args component object with object -> updateArgs()', () => {
const reactiveArgs = reactive({
objectArg: { argFoo: 'foo' },
}); // get reference to reactiveArgs or create a new one;
expectTypeOf(reactiveArgs).toMatchTypeOf<Record<string, any>>();
expectTypeOf(reactiveArgs).toEqualTypeOf<{
objectArg: { argFoo: string };
}>();

const newArgs = { objectArg: { argFoo: 'bar' } };
updateArgs(reactiveArgs, newArgs);

expect(reactiveArgs).toEqual({ objectArg: { argFoo: 'bar' } });
});

test('update reactive Args component no arg with all args -> updateArgs()', () => {
const reactiveArgs = reactive({ objectArg: { argFoo: 'foo' } }); // get reference to reactiveArgs or create a new one;
expectTypeOf(reactiveArgs).toMatchTypeOf<Record<string, any>>();
expectTypeOf(reactiveArgs).toEqualTypeOf<{
objectArg: { argFoo: string };
}>();

const newArgs = { objectArg: { argFoo: 'bar' } };
updateArgs(reactiveArgs, newArgs);

expect(reactiveArgs).toEqual({ objectArg: { argFoo: 'bar' } });
});
});
111 changes: 72 additions & 39 deletions code/renderers/vue3/src/render.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/* eslint-disable no-param-reassign */
import { createApp, h, reactive } from 'vue';
import { createApp, h, isReactive, isVNode, reactive } from 'vue';
import type { RenderContext, ArgsStoryFn } from '@storybook/types';
import type { Args, StoryContext } from '@storybook/csf';
import type { StoryFnVueReturnType, VueRenderer } from './types';

import type { VueRenderer, StoryFnVueReturnType } from './types';

export const render: ArgsStoryFn<VueRenderer> = (props, context) => {
const { id, component: Component } = context;
Expand All @@ -12,7 +13,7 @@ export const render: ArgsStoryFn<VueRenderer> = (props, context) => {
);
}

return h(Component, props, getSlots(props, context));
return h(Component, props, generateSlots(context));
};

let setupFunction = (_app: any) => {};
Expand All @@ -22,65 +23,84 @@ export const setup = (fn: (app: any) => void) => {

const map = new Map<
VueRenderer['canvasElement'],
{ vueApp: ReturnType<typeof createApp>; reactiveArgs: any }
{
vueApp: ReturnType<typeof createApp>;
reactiveArgs: Args;
}
>();

const elementMap = new Map<VueRenderer['canvasElement'], StoryFnVueReturnType>();

export function renderToCanvas(
{ storyFn, forceRemount, showMain, showException, storyContext }: RenderContext<VueRenderer>,
{ storyFn, forceRemount, showMain, showException, storyContext, id }: RenderContext<VueRenderer>,
canvasElement: VueRenderer['canvasElement']
) {
// fetch the story with the updated context (with reactive args)
storyContext.args = reactive(storyContext.args);
const element: StoryFnVueReturnType = storyFn();
elementMap.set(canvasElement, element);

const props = (element as any).render?.().props;
const reactiveArgs = props ? reactive(props) : storyContext.args;

const existingApp = map.get(canvasElement);

// if the story is already rendered and we are not forcing a remount, we just update the reactive args
if (existingApp && !forceRemount) {
updateArgs(existingApp.reactiveArgs, reactiveArgs);
// normally storyFn should be call once only in setup function,but because the nature of react and how storybook rendering the decorators
// we need to call here to run the decorators again
// i may wrap each decorator in memoized function to avoid calling it if the args are not changed
const element = storyFn(); // call the story function to get the root element with all the decorators
const args = getArgs(element, storyContext); // get args in case they are altered by decorators otherwise use the args from the context

updateArgs(existingApp.reactiveArgs, args);
return () => {
teardown(existingApp.vueApp, canvasElement);
};
}

if (existingApp && forceRemount) teardown(existingApp.vueApp, canvasElement);

const storybookApp = createApp({
render() {
const renderedElement: any = elementMap.get(canvasElement);
const current = renderedElement && renderedElement.template ? renderedElement : element;
map.set(canvasElement, { vueApp: storybookApp, reactiveArgs });
return h(current, reactiveArgs);
// create vue app for the story
const vueApp = createApp({
setup() {
storyContext.args = reactive(storyContext.args);
chakAs3 marked this conversation as resolved.
Show resolved Hide resolved
const rootElement = storyFn(); // call the story function to get the root element with all the decorators
const args = getArgs(rootElement, storyContext); // get args in case they are altered by decorators otherwise use the args from the context
const appState = {
vueApp,
reactiveArgs: reactive(args),
};
map.set(canvasElement, appState);

return () => {
return h(rootElement, appState.reactiveArgs);
};
},
});

storybookApp.config.errorHandler = (e: unknown) => showException(e as Error);
setupFunction(storybookApp);
storybookApp.mount(canvasElement);
vueApp.config.errorHandler = (e: unknown) => showException(e as Error);
setupFunction(vueApp);
vueApp.mount(canvasElement);

showMain();
return () => {
teardown(storybookApp, canvasElement);
teardown(vueApp, canvasElement);
};
}

/**
* get the slots as functions to be rendered
* @param props
* generate slots for default story without render function template
* @param context
*/

function getSlots(props: Args, context: StoryContext<VueRenderer, Args>) {
function generateSlots(context: StoryContext<VueRenderer, Args>) {
const { argTypes } = context;
const slots = Object.entries(props)
const slots = Object.entries(argTypes)
.filter(([key, value]) => argTypes[key]?.table?.category === 'slots')
.map(([key, value]) => [key, typeof value === 'function' ? value : () => value]);
.map(([key, value]) => {
const slotValue = context.args[key];
return [key, typeof slotValue === 'function' ? slotValue : () => slotValue];
});

return Object.fromEntries(slots);
return reactive(Object.fromEntries(slots));
}
/**
* get the args from the root element props if it is a vnode otherwise from the context
* @param element is the root element of the story
* @param storyContext is the story context
*/

function getArgs(element: StoryFnVueReturnType, storyContext: StoryContext<VueRenderer, Args>) {
return element.props && isVNode(element) ? element.props : storyContext.args;
}

/**
Expand All @@ -89,14 +109,27 @@ function getSlots(props: Args, context: StoryContext<VueRenderer, Args>) {
* @param nextArgs
* @returns
*/
function updateArgs(reactiveArgs: Args, nextArgs: Args) {
if (!nextArgs) return;
Object.keys(reactiveArgs).forEach((key) => {
delete reactiveArgs[key];
export function updateArgs(reactiveArgs: Args, nextArgs: Args) {
if (Object.keys(nextArgs).length === 0) return;
const currentArgs = isReactive(reactiveArgs) ? reactiveArgs : reactive(reactiveArgs);
// delete all args in currentArgs that are not in nextArgs
Object.keys(currentArgs).forEach((key) => {
if (!(key in nextArgs)) {
delete currentArgs[key];
}
});
Object.assign(reactiveArgs, nextArgs);
// update currentArgs with nextArgs
Object.assign(currentArgs, nextArgs);
}

/**
* unmount the vue app
* @param storybookApp
* @param canvasElement
* @returns void
* @private
* */

function teardown(
storybookApp: ReturnType<typeof createApp>,
canvasElement: VueRenderer['canvasElement']
Expand Down
18 changes: 18 additions & 0 deletions code/renderers/vue3/template/stories/BaseLayout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<script setup lang="ts">
defineProps({
label: String,
});
</script>
<template>
<div>
<header data-testid="header-slot">
<slot name="header" title="Header title from the slot"></slot>
</header>
<main data-testid="default-slot">
<slot></slot>
</main>
<footer data-testid="footer-slot">
<slot name="footer"></slot>
</footer>
</div>
</template>
Loading