Skip to content

Commit

Permalink
Merge pull request #22229 from storybookjs/kasper/vue3-reactivity-v6-…
Browse files Browse the repository at this point in the history
…compatible

Vue3: Rollback v7 breaking change and keep reactive v6-compatible API
  • Loading branch information
shilman authored Apr 28, 2023
2 parents fbde656 + 7b92734 commit 7e197e1
Show file tree
Hide file tree
Showing 8 changed files with 19 additions and 103 deletions.
13 changes: 3 additions & 10 deletions code/renderers/vue3/src/decorateStory.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { ConcreteComponent, Component, ComponentOptions } from 'vue';
import type { Component, ComponentOptions, ConcreteComponent } from 'vue';
import { h } from 'vue';
import type { DecoratorFunction, StoryContext, LegacyStoryFn, Args } from '@storybook/types';
import type { DecoratorFunction, LegacyStoryFn, StoryContext } 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 Down Expand Up @@ -45,7 +43,6 @@ 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 @@ -55,20 +52,16 @@ export function decorateStory(
...context,
...sanitizeStoryContextUpdate(update),
});

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

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

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

const innerStory = () => h(story!, context.args);
const innerStory = () => h(story!);
return prepare(decoratedStory, innerStory) as VueRenderer['storyResult'];
},
(context) => prepare(storyFn(context)) as LegacyStoryFn<VueRenderer>
Expand Down
32 changes: 7 additions & 25 deletions code/renderers/vue3/src/render.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
/* eslint-disable no-param-reassign */
import type { App, ConcreteComponent } from 'vue';
import type { App } from 'vue';
import { createApp, h, reactive, isVNode, isReactive } from 'vue';
import type { RenderContext, ArgsStoryFn } from '@storybook/types';
import type { ArgsStoryFn, RenderContext } from '@storybook/types';
import type { Args, StoryContext } from '@storybook/csf';

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

const slotsMap = new Map<
StoryID,
{
component?: Omit<ConcreteComponent<any>, 'props'>;
reactiveSlots?: Args;
}
>();
import type { StoryFnVueReturnType, StoryID, VueRenderer } from './types';

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

return h(Component, props, createOrUpdateSlots(context));
return () => h(Component, props, generateSlots(context));
};

// set of setup functions that will be called when story is created
Expand Down Expand Up @@ -82,7 +74,9 @@ export function renderToCanvas(
map.set(canvasElement, appState);

return () => {
return h(rootElement, appState.reactiveArgs);
// not passing args here as props
// treat the rootElement as a component without props
return h(rootElement);
};
},
});
Expand Down Expand Up @@ -157,15 +151,3 @@ function teardown(
storybookApp?.unmount();
if (map.has(canvasElement)) map.delete(canvasElement);
}

function createOrUpdateSlots(context: StoryContext<VueRenderer, Args>) {
const { id: storyID, component } = context;
const slots = generateSlots(context);
if (slotsMap.has(storyID)) {
const app = slotsMap.get(storyID);
if (app?.reactiveSlots) updateArgs(app.reactiveSlots, slots);
return app?.reactiveSlots;
}
slotsMap.set(storyID, { component, reactiveSlots: slots });
return slots;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ export {
NoDecorators,
DecoratorFunctionalComponent,
DecoratorFunctionalComponentArgsFromContext,
DecoratorFunctionalComponentArgsFromProps,
DecoratorComponentOptions,
DecoratorComponentOptionsArgsFromData,
DecoratorComponentOptionsArgsFromProps,
} from './ReactiveDecorators.stories';
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ export {
NoDecorators,
DecoratorFunctionalComponent,
DecoratorFunctionalComponentArgsFromContext,
DecoratorFunctionalComponentArgsFromProps,
DecoratorComponentOptions,
DecoratorComponentOptionsArgsFromData,
DecoratorComponentOptionsArgsFromProps,
} from './ReactiveDecorators.stories';

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@ export const ReactiveHtmlWrapper: Story = {
};

// to test that Simple html Decorators in CSF2 format are applied correctly in reactive mode
const ReactiveCSF2WrapperTempl: StoryFn = (args, { argTypes }) => ({
const ReactiveCSF2WrapperTempl: StoryFn = (args) => ({
components: { ReactiveArgs },
props: Object.keys(argTypes),
template: '<ReactiveArgs v-bind="$props" />',
setup() {
return { args };
},
template: '<ReactiveArgs v-bind="args" />',
});

export const ReactiveCSF2Wrapper = ReactiveCSF2WrapperTempl.bind({});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const DecoratorFunctionalComponent: Story = {
decorators: [
(storyFn, context) => {
const story = storyFn();
return () => h('div', [h('h2', ['Decorator not using args']), [h(story, context.args)]]);
return () => h('div', [h('h2', ['Decorator not using args']), [h(story)]]);
},
],
};
Expand All @@ -61,20 +61,7 @@ export const DecoratorFunctionalComponentArgsFromContext: Story = {
(storyFn, context) => {
const story = storyFn();
return () =>
h('div', [
h('h2', ['Decorator using args.label: ', context.args.label]),
[h(story, context.args)],
]);
},
],
};

export const DecoratorFunctionalComponentArgsFromProps: Story = {
decorators: [
(storyFn, context) => {
const story = storyFn();
return (args) =>
h('div', [h('h2', `Decorator using args.label: ${args.label}`), h(story, context.args)]);
h('div', [h('h2', ['Decorator using args.label: ', context.args.label]), [h(story)]]);
},
],
};
Expand All @@ -99,14 +86,3 @@ export const DecoratorComponentOptionsArgsFromData: Story = {
},
],
};

export const DecoratorComponentOptionsArgsFromProps: Story = {
decorators: [
(storyFn, context) => {
return {
props: ['label'],
template: '<div><h2>Decorator using label: {{label}}</h2><story /></div>',
};
},
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const CustomRender: Story = {
setup() {
return { args };
},
template: `<MySlotComponent :label="args.label" v-slot="slotProps">
template: `<MySlotComponent v-bind="args" v-slot="slotProps">
{{ slotProps.text }}, {{ slotProps.year }}
</MySlotComponent>`,
}),
Expand All @@ -73,7 +73,7 @@ export const CustomRenderUsingFunctionSlot: Story = {
setup() {
return { args };
},
template: `<MySlotComponent :label="args.label" v-slot="slotProps">
template: `<MySlotComponent v-bind="args" v-slot="slotProps">
{{args.default(slotProps)}}
</MySlotComponent>`,
}),
Expand Down

0 comments on commit 7e197e1

Please sign in to comment.