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: Support multiple setup functions #22170

Merged
merged 22 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
de2a078
support add new functions to vue app setup
chakAs3 Apr 4, 2023
e1091be
merge vue3-source-fix
chakAs3 Apr 5, 2023
28bded3
add stories test for provide/inject and using setup in different places
chakAs3 Apr 5, 2023
bb660b1
Merge branch 'next' of https://github.com/storybookjs/storybook into …
chakAs3 Apr 5, 2023
a920749
merging back changes from next to vue3-setup-functions
chakAs3 Apr 7, 2023
cd84008
Merge branch 'next' of https://github.com/storybookjs/storybook into …
chakAs3 Apr 7, 2023
497dd8b
Merge branch 'next' into chaks/vue3-setup-functions
chakAs3 Apr 10, 2023
4654108
fix merge conflict + add use case of new setup function impl
chakAs3 Apr 10, 2023
2bfd291
Merge branch 'next' of https://github.com/storybookjs/storybook into …
chakAs3 Apr 13, 2023
434593e
Merge branch 'next' of https://github.com/storybookjs/storybook into …
chakAs3 Apr 19, 2023
451430a
use preview.ts instead of preview.js in renderers/vue3/ tests
chakAs3 Apr 19, 2023
0b5fc58
Merge branch 'next' of https://github.com/storybookjs/storybook into …
chakAs3 Apr 19, 2023
5cb4271
fix type check, augment vue module
chakAs3 Apr 20, 2023
8156a80
Update docs/snippets/common/storybook-upgrade-prerelease.pnpm.js.mdx
chakAs3 Apr 20, 2023
50f8c6c
change the tests to be more generic
chakAs3 Apr 20, 2023
d9237f3
Merge branch 'chaks/vue3-setup-functions' of https://github.com/story…
chakAs3 Apr 20, 2023
876c9ae
refactory tests to be more generic
chakAs3 Apr 22, 2023
1a13e65
merge next branch into chaks/vue3-setup-functions
chakAs3 Apr 22, 2023
5d873d2
add some comments
chakAs3 Apr 22, 2023
f9c4bc4
Merge branch 'next' into chaks/vue3-setup-functions
chakAs3 Apr 24, 2023
c7ed94e
Merge branch 'next' into chaks/vue3-setup-functions
kasperpeulen Apr 25, 2023
6f6b1f0
Merge branch 'next' into chaks/vue3-setup-functions
shilman Apr 25, 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
22 changes: 16 additions & 6 deletions code/renderers/vue3/src/render.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-param-reassign */
import type { ConcreteComponent } from 'vue';
import { createApp, h, isReactive, isVNode, reactive } from 'vue';
import type { App, ConcreteComponent } from 'vue';
import { createApp, h, reactive, isVNode, isReactive } from 'vue';
import type { RenderContext, ArgsStoryFn } from '@storybook/types';
import type { Args, StoryContext } from '@storybook/csf';

Expand All @@ -25,9 +25,18 @@ export const render: ArgsStoryFn<VueRenderer> = (props, context) => {
return h(Component, props, createOrUpdateSlots(context));
};

let setupFunction = (_app: any) => {};
export const setup = (fn: (app: any) => void) => {
setupFunction = fn;
// set of setup functions that will be called when story is created
const setupFunctions = new Set<(app: App, storyContext?: StoryContext<VueRenderer>) => void>();
/** add a setup function to set that will be call when story is created a d
*
* @param fn
*/
export const setup = (fn: (app: App, storyContext?: StoryContext<VueRenderer>) => void) => {
setupFunctions.add(fn);
chakAs3 marked this conversation as resolved.
Show resolved Hide resolved
};

const runSetupFunctions = (app: App, storyContext: StoryContext<VueRenderer>) => {
setupFunctions.forEach((fn) => fn(app, storyContext));
};

const map = new Map<
Expand Down Expand Up @@ -77,8 +86,9 @@ export function renderToCanvas(
};
},
});

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

showMain();
Expand Down
52 changes: 52 additions & 0 deletions code/renderers/vue3/template/stories/GlobalSetup.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { expect } from '@storybook/jest';
import type { Meta, StoryObj } from 'renderers/vue3/src';
import { within } from '@storybook/testing-library';
import { inject } from 'vue';
import GlobalSetup from './GlobalSetup.vue';

const meta: Meta = {
component: GlobalSetup,
argTypes: {},
render: (args: any) => ({
// Components used in your story `template` are defined in the `components` object
components: { GlobalUsage: GlobalSetup },
// The story's `args` need to be mapped into the template through the `setup()` method
setup() {
const color = inject('someColor', 'red'); // <-- this is the global setup from .storybook/preview.ts
return { args: { ...args, backgroundColor: color } };
},
// And then the `args` are bound to your component with `v-bind="args"`
template: '<global-usage v-bind="args" />',
}),
} satisfies Meta<typeof GlobalSetup>;
export default meta;

type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
primary: true,
label: 'someColor injected from .storybook/preview.ts',
},
play: async ({ canvasElement, id }) => {
const canvas = within(canvasElement);

const button = await canvas.getByRole('button');
await expect(button).toHaveStyle('background-color: rgb(0, 128, 0)'); // <-- this provide themeColor = green from .storybook/preview.ts

const h2 = await canvas.getByRole('heading', { level: 2 });
await expect(h2).toHaveTextContent('Hi Story! from some plugin your name is Primary!');

const h3 = await canvas.getByRole('heading', { level: 3 });
await expect(h3).toHaveTextContent('Hello Story! from some plugin your name is Primary!');

const h4 = await canvas.getByRole('heading', { level: 4 });
await expect(h4).toHaveTextContent('Welcome Story! from some plugin your name is Primary!');
},
};

export const Secondary: Story = {
args: {
label: 'someColor injected from .storybook/preview.ts',
},
};
6 changes: 6 additions & 0 deletions code/renderers/vue3/template/stories/GlobalSetup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<template>
<global-button v-bind="$attrs"> </global-button>
<h2> Hi message: {{ $greetingMessage('hi') }}</h2>
<h3> Hello message: {{ $greetingMessage('hello') }}</h3>
<h4> Welcome message: {{ $greetingMessage('welcome') }}</h4>
</template>
2 changes: 1 addition & 1 deletion code/renderers/vue3/template/stories/GlobalUsage.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<template>
<global-button v-bind="$props" />
<global-button />
</template>
13 changes: 0 additions & 13 deletions code/renderers/vue3/template/stories/preview.js

This file was deleted.

51 changes: 51 additions & 0 deletions code/renderers/vue3/template/stories/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { App, Plugin } from 'vue';
import type { StoryContext } from '@storybook/csf';
import { global as globalThis } from '@storybook/global';
// eslint-disable-next-line import/no-extraneous-dependencies
import { setup, type VueRenderer } from '@storybook/vue3';

declare module 'vue' {
interface ComponentCustomProperties {
$greetingMessage: (key: string) => string;
}
}

declare global {
// eslint-disable-next-line no-var,vars-on-top
var Components: Record<string, any>;
}

const somePlugin: Plugin = {
install: (app: App, options) => {
// inject a globally available $greetingText() method
// eslint-disable-next-line no-param-reassign
chakAs3 marked this conversation as resolved.
Show resolved Hide resolved
app.config.globalProperties.$greetingMessage = (key: string) => {
// retrieve a nested property in `options`
// using `key`
return options.greetings[key];
};
},
};
const someColor = 'someColor';

// add components to global scope
setup((app: App) => {
// This adds a component that can be used globally in stories
app.component('GlobalButton', globalThis.Components.Button);
});

// this adds a plugin to vue app that can be used globally in stories
setup((app: App, context?: StoryContext<VueRenderer>) => {
app.use(somePlugin, {
greetings: {
hello: `Hello Story! from some plugin your name is ${context?.name}!`,
welcome: `Welcome Story! from some plugin your name is ${context?.name}!`,
hi: `Hi Story! from some plugin your name is ${context?.name}!`,
},
});
});

// additonal setup to provide some propriety to the app
setup((app: App, context) => {
app.provide(someColor, 'green');
});