Export decorateStory from frameworks that have it.

And no longer emit warnings for exported `decorateStory` or `renderToDOM`
This commit is contained in:
Tom Coleman 2021-10-30 14:57:20 +11:00
parent f521a74479
commit d9483a05a8
11 changed files with 174 additions and 150 deletions

View File

@ -1,3 +1,4 @@
export { render, renderToDOM } from './render';
export { decorateStory } from './decorateStory';
export const parameters = { framework: 'angular' };

View File

@ -27,6 +27,8 @@ export default function decorateStory(
return returnDecorators;
}
export { decorateStory };
const prepareMain = (
story: AngularFramework['storyResult'],
context: StoryContext<AngularFramework>

View File

@ -1,3 +1,4 @@
export { renderToDOM } from './render';
export { decorateStory } from './decorators';
export const parameters = { framework: 'svelte' };

View File

@ -1,3 +1,4 @@
export { renderToDOM } from './render';
export { decorateStory } from './decorateStory';
export const parameters = { framework: 'vue' };

View File

@ -0,0 +1,85 @@
import Vue, { VueConstructor, ComponentOptions } from 'vue';
import { DecoratorFunction, StoryContext, LegacyStoryFn } from '@storybook/csf';
import { sanitizeStoryContextUpdate } from '@storybook/store';
import { StoryFnVueReturnType } from './types';
import { VueFramework } from './types-6-0';
import { extractProps } from './util';
import { VALUES } from './render';
export const WRAPS = 'STORYBOOK_WRAPS';
function prepare(
rawStory: StoryFnVueReturnType,
innerStory?: VueConstructor
): VueConstructor | null {
let story: ComponentOptions<Vue> | VueConstructor;
if (typeof rawStory === 'string') {
story = { template: rawStory };
} else if (rawStory != null) {
story = rawStory as ComponentOptions<Vue>;
} else {
return null;
}
// @ts-ignore
// eslint-disable-next-line no-underscore-dangle
if (!story._isVue) {
if (innerStory) {
story.components = { ...(story.components || {}), story: innerStory };
}
story = Vue.extend(story);
// @ts-ignore // https://github.com/storybookjs/storybook/pull/7578#discussion_r307984824
} else if (story.options[WRAPS]) {
return story as VueConstructor;
}
return Vue.extend({
// @ts-ignore // https://github.com/storybookjs/storybook/pull/7578#discussion_r307985279
[WRAPS]: story,
// @ts-ignore // https://github.com/storybookjs/storybook/pull/7578#discussion_r307984824
[VALUES]: { ...(innerStory ? innerStory.options[VALUES] : {}), ...extractProps(story) },
functional: true,
render(h, { data, parent, children }) {
return h(
story,
{
...data,
// @ts-ignore // https://github.com/storybookjs/storybook/pull/7578#discussion_r307986196
props: { ...(data.props || {}), ...parent.$root[VALUES] },
},
children
);
},
});
}
export function decorateStory(
storyFn: LegacyStoryFn<VueFramework>,
decorators: DecoratorFunction<VueFramework>[]
): LegacyStoryFn<VueFramework> {
return decorators.reduce(
(decorated: LegacyStoryFn<VueFramework>, decorator) => (
context: StoryContext<VueFramework>
) => {
let story;
const decoratedStory = decorator((update) => {
story = decorated({ ...context, ...sanitizeStoryContextUpdate(update) });
return story;
}, context);
if (!story) {
story = decorated(context);
}
if (decoratedStory === story) {
return story;
}
return prepare(decoratedStory, story as any);
},
(context) => prepare(storyFn(context))
);
}

View File

@ -1,93 +1,13 @@
/* eslint-disable prefer-destructuring */
import Vue, { VueConstructor, ComponentOptions } from 'vue';
import { start } from '@storybook/core/client';
import { DecoratorFunction, StoryContext, LegacyStoryFn } from '@storybook/csf';
import { ClientStoryApi, Loadable } from '@storybook/addons';
import { sanitizeStoryContextUpdate } from '@storybook/store';
import './globals';
import { IStorybookSection, StoryFnVueReturnType } from './types';
import { IStorybookSection } from './types';
import { VueFramework } from './types-6-0';
import { renderToDOM } from './render';
import { decorateStory } from './decorateStory';
import { renderToDOM, VALUES } from './render';
import { extractProps } from './util';
export const WRAPS = 'STORYBOOK_WRAPS';
function prepare(
rawStory: StoryFnVueReturnType,
innerStory?: VueConstructor
): VueConstructor | null {
let story: ComponentOptions<Vue> | VueConstructor;
if (typeof rawStory === 'string') {
story = { template: rawStory };
} else if (rawStory != null) {
story = rawStory as ComponentOptions<Vue>;
} else {
return null;
}
// @ts-ignore
// eslint-disable-next-line no-underscore-dangle
if (!story._isVue) {
if (innerStory) {
story.components = { ...(story.components || {}), story: innerStory };
}
story = Vue.extend(story);
// @ts-ignore // https://github.com/storybookjs/storybook/pull/7578#discussion_r307984824
} else if (story.options[WRAPS]) {
return story as VueConstructor;
}
return Vue.extend({
// @ts-ignore // https://github.com/storybookjs/storybook/pull/7578#discussion_r307985279
[WRAPS]: story,
// @ts-ignore // https://github.com/storybookjs/storybook/pull/7578#discussion_r307984824
[VALUES]: { ...(innerStory ? innerStory.options[VALUES] : {}), ...extractProps(story) },
functional: true,
render(h, { data, parent, children }) {
return h(
story,
{
...data,
// @ts-ignore // https://github.com/storybookjs/storybook/pull/7578#discussion_r307986196
props: { ...(data.props || {}), ...parent.$root[VALUES] },
},
children
);
},
});
}
function decorateStory(
storyFn: LegacyStoryFn<VueFramework>,
decorators: DecoratorFunction<VueFramework>[]
): LegacyStoryFn<VueFramework> {
return decorators.reduce(
(decorated: LegacyStoryFn<VueFramework>, decorator) => (
context: StoryContext<VueFramework>
) => {
let story;
const decoratedStory = decorator((update) => {
story = decorated({ ...context, ...sanitizeStoryContextUpdate(update) });
return story;
}, context);
if (!story) {
story = decorated(context);
}
if (decoratedStory === story) {
return story;
}
return prepare(decoratedStory, story as any);
},
(context) => prepare(storyFn(context))
);
}
const framework = 'vue';
interface ClientApi extends ClientStoryApi<VueFramework['storyResult']> {

View File

@ -1,3 +1,4 @@
export { renderToDOM } from './render';
export { decorateStory } from './decorateStory';
export const parameters = { framework: 'vue3' };

View File

@ -0,0 +1,70 @@
import type { ConcreteComponent, Component, ComponentOptions } from 'vue';
import { h } from 'vue';
import { DecoratorFunction, StoryContext, LegacyStoryFn } from '@storybook/csf';
import { sanitizeStoryContextUpdate } from '@storybook/store';
import { VueFramework } from './types-6-0';
/*
This normalizes a functional component into a render method in ComponentOptions.
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;
}
function prepare(
rawStory: VueFramework['storyResult'],
innerStory?: ConcreteComponent
): Component | null {
const story = rawStory as ComponentOptions;
if (story == null) {
return null;
}
if (innerStory) {
return {
// Normalize so we can always spread an object
...normalizeFunctionalComponent(story),
components: { ...(story.components || {}), story: innerStory },
};
}
return {
render() {
return h(story);
},
};
}
export function decorateStory(
storyFn: LegacyStoryFn<VueFramework>,
decorators: DecoratorFunction<VueFramework>[]
): LegacyStoryFn<VueFramework> {
return decorators.reduce(
(decorated: LegacyStoryFn<VueFramework>, decorator) => (
context: StoryContext<VueFramework>
) => {
let story: VueFramework['storyResult'];
const decoratedStory: VueFramework['storyResult'] = decorator((update) => {
story = decorated({ ...context, ...sanitizeStoryContextUpdate(update) });
return story;
}, context);
if (!story) {
story = decorated(context);
}
if (decoratedStory === story) {
return story;
}
return prepare(decoratedStory, story) as VueFramework['storyResult'];
},
(context) => prepare(storyFn(context)) as LegacyStoryFn<VueFramework>
);
}

View File

@ -1,79 +1,14 @@
import type { ConcreteComponent, Component, ComponentOptions, App } from 'vue';
import { h } from 'vue';
import type { App } from 'vue';
import { start } from '@storybook/core/client';
import { DecoratorFunction, StoryContext, LegacyStoryFn } from '@storybook/csf';
import { ClientStoryApi, Loadable } from '@storybook/addons';
import { sanitizeStoryContextUpdate } from '@storybook/store';
import './globals';
import { IStorybookSection } from './types';
import { VueFramework } from './types-6-0';
import { decorateStory } from './decorateStory';
import { renderToDOM, storybookApp } from './render';
/*
This normalizes a functional component into a render method in ComponentOptions.
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;
}
function prepare(
rawStory: VueFramework['storyResult'],
innerStory?: ConcreteComponent
): Component | null {
const story = rawStory as ComponentOptions;
if (story == null) {
return null;
}
if (innerStory) {
return {
// Normalize so we can always spread an object
...normalizeFunctionalComponent(story),
components: { ...(story.components || {}), story: innerStory },
};
}
return {
render() {
return h(story);
},
};
}
function decorateStory(
storyFn: LegacyStoryFn<VueFramework>,
decorators: DecoratorFunction<VueFramework>[]
): LegacyStoryFn<VueFramework> {
return decorators.reduce(
(decorated: LegacyStoryFn<VueFramework>, decorator) => (
context: StoryContext<VueFramework>
) => {
let story: VueFramework['storyResult'];
const decoratedStory: VueFramework['storyResult'] = decorator((update) => {
story = decorated({ ...context, ...sanitizeStoryContextUpdate(update) });
return story;
}, context);
if (!story) {
story = decorated(context);
}
if (decoratedStory === story) {
return story;
}
return prepare(decoratedStory, story) as VueFramework['storyResult'];
},
(context) => prepare(storyFn(context)) as LegacyStoryFn<VueFramework>
);
}
const framework = 'vue3';
interface ClientApi extends ClientStoryApi<VueFramework['storyResult']> {

View File

@ -41,6 +41,10 @@ Object.keys(config).forEach((key) => {
v[key] = value;
return addParameters(v, false);
}
case 'decorateStory':
case 'renderToDOM': {
return null; // This key is not handled directly in v6 mode.
}
default: {
// eslint-disable-next-line prefer-template
return console.log(key + ' was not supported :( !');

View File

@ -41,6 +41,10 @@ Object.keys(config).forEach((key) => {
v[key] = value;
return addParameters(v, false);
}
case 'decorateStory':
case 'renderToDOM': {
return null; // This key is not handled directly in v6 mode.
}
default: {
// eslint-disable-next-line prefer-template
return console.log(key + ' was not supported :( !');