From 4805b5685a9115529d892aa1a7aba47c59e91130 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 13 May 2021 15:03:18 +0200 Subject: [PATCH 01/16] working draft version --- addons/docs/src/blocks/Story.tsx | 105 +++++++++++------- app/react/src/client/preview/render.tsx | 13 ++- lib/core-client/src/preview/StoryRenderer.tsx | 93 +++++++++++++++- lib/core-events/src/index.ts | 4 + 4 files changed, 166 insertions(+), 49 deletions(-) diff --git a/addons/docs/src/blocks/Story.tsx b/addons/docs/src/blocks/Story.tsx index 6e239f072ca1..4b4e1413f1ca 100644 --- a/addons/docs/src/blocks/Story.tsx +++ b/addons/docs/src/blocks/Story.tsx @@ -1,6 +1,7 @@ -import React, { FunctionComponent, ReactNode, ElementType, ComponentProps } from 'react'; +import React, { FunctionComponent, ReactNode, ElementType, ComponentProps, useEffect } from 'react'; import { MDXProvider } from '@mdx-js/react'; import { resetComponents, Story as PureStory } from '@storybook/components'; +import { DOCS_TARGETTED_RENDER, DOCS_TARGETTED_DESTROY } from '@storybook/core-events'; import { toId, storyNameFromExport } from '@storybook/csf'; import { Args, BaseAnnotations } from '@storybook/addons'; import { CURRENT_SELECTION } from './types'; @@ -9,7 +10,7 @@ import { DocsContext, DocsContextProps } from './DocsContext'; export const storyBlockIdFromId = (storyId: string) => `story--${storyId}`; -type PureStoryProps = ComponentProps; +// type PureStoryProps = ComponentProps; type CommonProps = BaseAnnotations & { height?: string; @@ -41,53 +42,71 @@ export const lookupStoryId = ( storyNameFromExport(mdxStoryNameToKey[storyName]) ); -export const getStoryProps = (props: StoryProps, context: DocsContextProps): PureStoryProps => { - const { id } = props as StoryRefProps; - const { name } = props as StoryDefProps; - const inputId = id === CURRENT_SELECTION ? context.id : id; - const previewId = inputId || lookupStoryId(name, context); - const data = context.storyStore.fromId(previewId) || {}; - - const { height, inline } = props; - const { storyFn = undefined, name: storyName = undefined, parameters = {} } = data; - const { docs = {} } = parameters; - - if (docs.disable) { - return null; - } - - // prefer block props, then story parameters defined by the framework-specific settings and optionally overridden by users - const { inlineStories = false, iframeHeight = 100, prepareForInline } = docs; - const storyIsInline = typeof inline === 'boolean' ? inline : inlineStories; - if (storyIsInline && !prepareForInline) { - throw new Error( - `Story '${storyName}' is set to render inline, but no 'prepareForInline' function is implemented in your docs configuration!` - ); - } - - return { - parameters, - inline: storyIsInline, - id: previewId, - storyFn: prepareForInline && storyFn ? () => prepareForInline(storyFn, data) : storyFn, - height: height || (storyIsInline ? undefined : iframeHeight), - title: storyName, - }; +// export const getStoryProps = (props: StoryProps, context: DocsContextProps): PureStoryProps => { +// const { id } = props as StoryRefProps; +// const { name } = props as StoryDefProps; +// const inputId = id === CURRENT_SELECTION ? context.id : id; +// const previewId = inputId || lookupStoryId(name, context); +// const data = context.storyStore.fromId(previewId) || {}; + +// const { height, inline } = props; +// const { storyFn = undefined, name: storyName = undefined, parameters = {} } = data; +// const { docs = {} } = parameters; + +// if (docs.disable) { +// return null; +// } + +// // prefer block props, then story parameters defined by the framework-specific settings and optionally overridden by users +// const { inlineStories = false, iframeHeight = 100, prepareForInline } = docs; +// const storyIsInline = typeof inline === 'boolean' ? inline : inlineStories; + +// if (storyIsInline && !prepareForInline) { +// throw new Error( +// `Story '${storyName}' is set to render inline, but no 'prepareForInline' function is implemented in your docs configuration!` +// ); +// } + +// return { +// parameters, +// inline: storyIsInline, +// id: previewId, +// storyFn: prepareForInline && storyFn ? () => prepareForInline(storyFn, data) : storyFn, +// height: height || (storyIsInline ? undefined : iframeHeight), +// title: storyName, +// }; +// }; + +const Placeholder: FunctionComponent = ({ id, name, channel }) => { + const identifier = storyBlockIdFromId(id); + useEffect(() => { + channel.emit(DOCS_TARGETTED_RENDER, { identifier, id, name }); + return () => { + channel.emit(DOCS_TARGETTED_DESTROY, { identifier, id, name }); + console.log(`emit ${DOCS_TARGETTED_DESTROY}`); + }; + }); + + return ( +
+ loading story... +
+ ); }; const Story: FunctionComponent = (props) => ( {(context) => { - const storyProps = getStoryProps(props, context); - if (!storyProps) { - return null; - } + const { id } = props as StoryRefProps; + const { name } = props as StoryDefProps; + const inputId = id === CURRENT_SELECTION ? context.id : id; + const previewId = inputId || lookupStoryId(name, context); + console.log({ context, props }); + return ( -
- - - -
+ + + ); }}
diff --git a/app/react/src/client/preview/render.tsx b/app/react/src/client/preview/render.tsx index d2f5e5a05acd..8a19802f69a4 100644 --- a/app/react/src/client/preview/render.tsx +++ b/app/react/src/client/preview/render.tsx @@ -51,6 +51,7 @@ export default async function renderMain({ showMain, showException, forceRender, + targetDOMNode = rootEl, }: RenderContext) { const Story = unboundStoryFn as FunctionComponent; @@ -68,9 +69,17 @@ export default async function renderMain({ // This could leads to issues like below: // https://github.com/storybookjs/react-storybook/issues/81 // But forceRender means that it's the same story, so we want too keep the state in that case. + if (!targetDOMNode) { + debugger; + } + if (!forceRender) { - ReactDOM.unmountComponentAtNode(rootEl); + try { + ReactDOM.unmountComponentAtNode(targetDOMNode); + } catch (e) { + debugger; + } } - await render(element, rootEl); + await render(element, targetDOMNode); } diff --git a/lib/core-client/src/preview/StoryRenderer.tsx b/lib/core-client/src/preview/StoryRenderer.tsx index 262ac2b44fc8..7e5389c69de3 100644 --- a/lib/core-client/src/preview/StoryRenderer.tsx +++ b/lib/core-client/src/preview/StoryRenderer.tsx @@ -43,6 +43,8 @@ const ansiConverter = new AnsiToHtml({ * It is very much concerned with drawing to the screen and will do things like change classes * on the body etc. */ + +const cache = {}; export class StoryRenderer { render: RenderStoryFunction; @@ -77,6 +79,10 @@ export class StoryRenderer { this.channel.on(Events.STORY_ARGS_UPDATED, () => this.forceReRender()); this.channel.on(Events.GLOBALS_UPDATED, () => this.forceReRender()); this.channel.on(Events.FORCE_RE_RENDER, () => this.forceReRender()); + this.channel.on(Events.DOCS_TARGETTED_RENDER, (data) => { + console.log('DOCS_TARGETTED_RENDER received'); + this.renderDocsStoryOutOfContext(data); + }); } } @@ -84,6 +90,65 @@ export class StoryRenderer { this.renderCurrentStory(true); } + async renderDocsStoryOutOfContext({ id, identifier, name }) { + if (cache[id]) { + return; + } + cache[id] = true; + + const targetDOMNode = document.getElementById(identifier); + + if (!targetDOMNode) { + cache[id] = false; + return; + } + + const { + metadata, + context, + }: { + metadata: RenderMetadata; + context: RenderContextWithoutStoryContext; + } = this.getStoryMetadataAndContext(this.storyStore, id, 'docs', false); + + const { getDecorated, viewMode } = metadata; + + console.log({ metadata }); + if (viewMode !== 'docs') { + cache[id] = false; + + return; + } + + if (getDecorated) { + try { + const { applyLoaders, unboundStoryFn } = context; + console.time(`applyLoaders_${id}`); + const storyContext = await applyLoaders(); + console.timeEnd(`applyLoaders_${id}`); + const storyFn = () => unboundStoryFn(storyContext); + + console.time(`render_${id}`); + await this.render({ + ...context, + storyContext, + storyFn, + forceRender: true, + targetDOMNode, + }); + console.timeEnd(`render_${id}`); + + this.channel.emit(Events.STORY_RENDERED, id); + cache[id] = false; + } catch (err) { + cache[id] = false; + this.renderException(err); + } + } else { + console.error('could not find story to render', { id, name }); + } + } + async renderCurrentStory(forceRender: boolean) { const { storyStore } = this; @@ -95,6 +160,23 @@ export class StoryRenderer { const { storyId, viewMode: urlViewMode } = storyStore.getSelection() || {}; + const { + metadata, + context, + }: { + metadata: RenderMetadata; + context: RenderContextWithoutStoryContext; + } = this.getStoryMetadataAndContext(storyStore, storyId, urlViewMode, forceRender); + + await this.renderStoryIfChanged({ metadata, context }); + } + + private getStoryMetadataAndContext( + storyStore: StoryStore, + storyId: string, + urlViewMode: string, + forceRender: boolean + ) { const data = storyStore.fromId(storyId); const { kind, id, parameters = {}, getDecorated } = data || {}; const { docsOnly, layout } = parameters; @@ -109,7 +191,7 @@ export class StoryRenderer { this.applyLayout(metadata.viewMode === 'docs' ? 'fullscreen' : layout); const context: RenderContextWithoutStoryContext = { - id: storyId, // <- in case data is null, at least we'll know what we tried to render + id: storyId, ...data, forceRender, showMain: () => this.showMain(), @@ -117,8 +199,7 @@ export class StoryRenderer { this.renderError({ title, description }), showException: (err: Error) => this.renderException(err), }; - - await this.renderStoryIfChanged({ metadata, context }); + return { metadata, context }; } async renderStoryIfChanged({ @@ -158,6 +239,7 @@ export class StoryRenderer { case 'docs': if (kindChanged || viewModeChanged) { this.storyStore.cleanHooksForKind(previousMetadata.kind); + // debugger; ReactDOM.unmountComponentAtNode(document.getElementById('docs-root')); } break; @@ -165,6 +247,7 @@ export class StoryRenderer { default: if (previousMetadata && (storyChanged || viewModeChanged)) { this.storyStore.cleanHooks(previousMetadata.id); + // debugger; ReactDOM.unmountComponentAtNode(document.getElementById('root')); } } @@ -306,12 +389,14 @@ export class StoryRenderer { throw new Error('No `docs.container` set, did you run `addon-docs/preset`?'); } + // console.log('renderDocs', { context }); + const DocsContainer = docs.container || (({ children }: { children: Element }) => <>{children}); const Page = docs.page || NoDocs; // Docs context includes the storyStore. Probably it would be better if it didn't but that can be fixed in a later refactor ReactDOM.render( - + , document.getElementById('docs-root'), diff --git a/lib/core-events/src/index.ts b/lib/core-events/src/index.ts index bf7850305b44..cbc17d4a5838 100644 --- a/lib/core-events/src/index.ts +++ b/lib/core-events/src/index.ts @@ -34,6 +34,8 @@ enum events { STORIES_COLLAPSE_ALL = 'storiesCollapseAll', STORIES_EXPAND_ALL = 'storiesExpandAll', DOCS_RENDERED = 'docsRendered', + DOCS_TARGETTED_RENDER = 'docsTargettedRender', + DOCS_TARGETTED_DESTROY = 'docsTargettedRender', SHARED_STATE_CHANGED = 'sharedStateChanged', SHARED_STATE_SET = 'sharedStateSet', NAVIGATE_URL = 'navigateUrl', @@ -68,6 +70,8 @@ export const { STORIES_COLLAPSE_ALL, STORIES_EXPAND_ALL, DOCS_RENDERED, + DOCS_TARGETTED_RENDER, + DOCS_TARGETTED_DESTROY, SHARED_STATE_CHANGED, SHARED_STATE_SET, NAVIGATE_URL, From 0c61d625ec4f19249c7e29f5b29c66dcf9162994 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 13 May 2021 16:35:29 +0200 Subject: [PATCH 02/16] render stories out of context in docs mode fire an async event to render into placeholder removes need for prepareForInline and brings inline rendering of docs to all frameworks --- addons/docs/src/blocks/Story.tsx | 51 +++---------------- app/ember/src/client/preview/render.ts | 11 +++- app/react/src/client/preview/render.tsx | 4 +- lib/client-api/src/types.ts | 1 + lib/core-client/src/manager/index.ts | 4 +- lib/core-client/src/preview/StoryRenderer.tsx | 24 ++++----- 6 files changed, 30 insertions(+), 65 deletions(-) diff --git a/addons/docs/src/blocks/Story.tsx b/addons/docs/src/blocks/Story.tsx index 4b4e1413f1ca..83175d85d7c8 100644 --- a/addons/docs/src/blocks/Story.tsx +++ b/addons/docs/src/blocks/Story.tsx @@ -1,17 +1,15 @@ -import React, { FunctionComponent, ReactNode, ElementType, ComponentProps, useEffect } from 'react'; +import React, { FunctionComponent, ReactNode, ElementType, useEffect } from 'react'; import { MDXProvider } from '@mdx-js/react'; -import { resetComponents, Story as PureStory } from '@storybook/components'; +import { resetComponents } from '@storybook/components'; import { DOCS_TARGETTED_RENDER, DOCS_TARGETTED_DESTROY } from '@storybook/core-events'; import { toId, storyNameFromExport } from '@storybook/csf'; -import { Args, BaseAnnotations } from '@storybook/addons'; +import { Args, BaseAnnotations, addons } from '@storybook/addons'; import { CURRENT_SELECTION } from './types'; import { DocsContext, DocsContextProps } from './DocsContext'; export const storyBlockIdFromId = (storyId: string) => `story--${storyId}`; -// type PureStoryProps = ComponentProps; - type CommonProps = BaseAnnotations & { height?: string; inline?: boolean; @@ -42,48 +40,14 @@ export const lookupStoryId = ( storyNameFromExport(mdxStoryNameToKey[storyName]) ); -// export const getStoryProps = (props: StoryProps, context: DocsContextProps): PureStoryProps => { -// const { id } = props as StoryRefProps; -// const { name } = props as StoryDefProps; -// const inputId = id === CURRENT_SELECTION ? context.id : id; -// const previewId = inputId || lookupStoryId(name, context); -// const data = context.storyStore.fromId(previewId) || {}; - -// const { height, inline } = props; -// const { storyFn = undefined, name: storyName = undefined, parameters = {} } = data; -// const { docs = {} } = parameters; - -// if (docs.disable) { -// return null; -// } - -// // prefer block props, then story parameters defined by the framework-specific settings and optionally overridden by users -// const { inlineStories = false, iframeHeight = 100, prepareForInline } = docs; -// const storyIsInline = typeof inline === 'boolean' ? inline : inlineStories; - -// if (storyIsInline && !prepareForInline) { -// throw new Error( -// `Story '${storyName}' is set to render inline, but no 'prepareForInline' function is implemented in your docs configuration!` -// ); -// } - -// return { -// parameters, -// inline: storyIsInline, -// id: previewId, -// storyFn: prepareForInline && storyFn ? () => prepareForInline(storyFn, data) : storyFn, -// height: height || (storyIsInline ? undefined : iframeHeight), -// title: storyName, -// }; -// }; - -const Placeholder: FunctionComponent = ({ id, name, channel }) => { +const Placeholder: FunctionComponent = ({ id, name }) => { + const channel = addons.getChannel(); const identifier = storyBlockIdFromId(id); useEffect(() => { channel.emit(DOCS_TARGETTED_RENDER, { identifier, id, name }); return () => { channel.emit(DOCS_TARGETTED_DESTROY, { identifier, id, name }); - console.log(`emit ${DOCS_TARGETTED_DESTROY}`); + // TODO this does nothing, should it do something though? }; }); @@ -101,11 +65,10 @@ const Story: FunctionComponent = (props) => ( const { name } = props as StoryDefProps; const inputId = id === CURRENT_SELECTION ? context.id : id; const previewId = inputId || lookupStoryId(name, context); - console.log({ context, props }); return ( - + ); }} diff --git a/app/ember/src/client/preview/render.ts b/app/ember/src/client/preview/render.ts index a7f980f8e6f6..b1bdd9a05e3a 100644 --- a/app/ember/src/client/preview/render.ts +++ b/app/ember/src/client/preview/render.ts @@ -57,7 +57,14 @@ function render(options: OptionsArgs, el: ElementArgs) { }); } -export default function renderMain({ storyFn, kind, name, showMain, showError }: RenderContext) { +export default function renderMain({ + storyFn, + kind, + name, + showMain, + showError, + targetDOMNode = rootEl, +}: RenderContext) { const element = storyFn(); if (!element) { @@ -74,5 +81,5 @@ export default function renderMain({ storyFn, kind, name, showMain, showError }: } showMain(); - render(element, rootEl); + render(element, targetDOMNode); } diff --git a/app/react/src/client/preview/render.tsx b/app/react/src/client/preview/render.tsx index 8a19802f69a4..95084450521c 100644 --- a/app/react/src/client/preview/render.tsx +++ b/app/react/src/client/preview/render.tsx @@ -4,7 +4,7 @@ import ReactDOM from 'react-dom'; import { StoryContext, RenderContext } from './types'; -const rootEl = document ? document.getElementById('root') : null; +const rootElement = document ? document.getElementById('root') : null; const render = (node: ReactElement, el: Element) => new Promise((resolve) => { @@ -51,7 +51,7 @@ export default async function renderMain({ showMain, showException, forceRender, - targetDOMNode = rootEl, + targetDOMNode = rootElement, }: RenderContext) { const Story = unboundStoryFn as FunctionComponent; diff --git a/lib/client-api/src/types.ts b/lib/client-api/src/types.ts index cdd76b118082..da1da30452df 100644 --- a/lib/client-api/src/types.ts +++ b/lib/client-api/src/types.ts @@ -109,6 +109,7 @@ export type RenderContextWithoutStoryContext = StoreItem & { showMain: () => void; showError: (error: { title: string; description: string }) => void; showException: (err: Error) => void; + targetDOMNode?: ReturnType; }; export type RenderContext = RenderContextWithoutStoryContext & { diff --git a/lib/core-client/src/manager/index.ts b/lib/core-client/src/manager/index.ts index e63acd2424a1..ae1ecd45d907 100644 --- a/lib/core-client/src/manager/index.ts +++ b/lib/core-client/src/manager/index.ts @@ -4,6 +4,6 @@ import Provider from './provider'; import { importPolyfills } from './conditional-polyfills'; importPolyfills().then(() => { - const rootEl = document.getElementById('root'); - renderStorybookUI(rootEl, new Provider()); + const rootElement = document.getElementById('root'); + renderStorybookUI(rootElement, new Provider()); }); diff --git a/lib/core-client/src/preview/StoryRenderer.tsx b/lib/core-client/src/preview/StoryRenderer.tsx index 7e5389c69de3..3828afbc56d8 100644 --- a/lib/core-client/src/preview/StoryRenderer.tsx +++ b/lib/core-client/src/preview/StoryRenderer.tsx @@ -44,7 +44,7 @@ const ansiConverter = new AnsiToHtml({ * on the body etc. */ -const cache = {}; +const cache: Record = {}; export class StoryRenderer { render: RenderStoryFunction; @@ -80,8 +80,9 @@ export class StoryRenderer { this.channel.on(Events.GLOBALS_UPDATED, () => this.forceReRender()); this.channel.on(Events.FORCE_RE_RENDER, () => this.forceReRender()); this.channel.on(Events.DOCS_TARGETTED_RENDER, (data) => { - console.log('DOCS_TARGETTED_RENDER received'); - this.renderDocsStoryOutOfContext(data); + setTimeout(() => { + this.renderDocsStoryOutOfContext(data); + }, 1); }); } } @@ -90,7 +91,8 @@ export class StoryRenderer { this.renderCurrentStory(true); } - async renderDocsStoryOutOfContext({ id, identifier, name }) { + async renderDocsStoryOutOfContext({ id, identifier, name }: Record) { + // TODO, the channel seems to invoke this twice if (cache[id]) { return; } @@ -111,14 +113,7 @@ export class StoryRenderer { context: RenderContextWithoutStoryContext; } = this.getStoryMetadataAndContext(this.storyStore, id, 'docs', false); - const { getDecorated, viewMode } = metadata; - - console.log({ metadata }); - if (viewMode !== 'docs') { - cache[id] = false; - - return; - } + const { getDecorated } = metadata; if (getDecorated) { try { @@ -139,14 +134,13 @@ export class StoryRenderer { console.timeEnd(`render_${id}`); this.channel.emit(Events.STORY_RENDERED, id); - cache[id] = false; } catch (err) { - cache[id] = false; this.renderException(err); } } else { console.error('could not find story to render', { id, name }); } + cache[id] = false; } async renderCurrentStory(forceRender: boolean) { @@ -184,7 +178,7 @@ export class StoryRenderer { const metadata: RenderMetadata = { id, kind, - viewMode: docsOnly ? 'docs' : urlViewMode, + viewMode: (docsOnly ? 'docs' : urlViewMode) as RenderMetadata['viewMode'], getDecorated, }; From ec05cb581c10a325b6b4e55105657e74c8d60474 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 13 May 2021 16:40:08 +0200 Subject: [PATCH 03/16] add support for new out-of-context rendering to all frameworks --- app/ember/src/client/preview/render.ts | 8 ++++---- app/html/src/client/preview/render.ts | 11 ++++++----- app/lit/src/client/preview/render.ts | 13 ++++++++++--- app/preact/src/client/preview/render.tsx | 11 ++++++----- app/server/src/client/preview/render.ts | 11 ++++++----- app/svelte/src/client/preview/render.ts | 16 +++++++++++----- app/vue/src/client/preview/render.ts | 5 ++++- app/vue3/src/client/preview/render.ts | 4 +++- app/web-components/src/client/preview/render.ts | 17 +++++++++-------- 9 files changed, 59 insertions(+), 37 deletions(-) diff --git a/app/ember/src/client/preview/render.ts b/app/ember/src/client/preview/render.ts index b1bdd9a05e3a..73f6fecbebae 100644 --- a/app/ember/src/client/preview/render.ts +++ b/app/ember/src/client/preview/render.ts @@ -4,12 +4,12 @@ import { RenderContext, ElementArgs, OptionsArgs } from './types'; declare let Ember: any; -const rootEl = document.getElementById('root'); +const rootElement = document.getElementById('root'); const config = globalWindow.require(`${globalWindow.STORYBOOK_NAME}/config/environment`); const app = globalWindow.require(`${globalWindow.STORYBOOK_NAME}/app`).default.create({ autoboot: false, - rootElement: rootEl, + rootElement, ...config.APP, }); @@ -63,7 +63,7 @@ export default function renderMain({ name, showMain, showError, - targetDOMNode = rootEl, + targetDOMNode = rootElement, }: RenderContext) { const element = storyFn(); @@ -81,5 +81,5 @@ export default function renderMain({ } showMain(); - render(element, targetDOMNode); + render(element, { el: targetDOMNode }); } diff --git a/app/html/src/client/preview/render.ts b/app/html/src/client/preview/render.ts index 28aa86ad927c..09e2e55d8a79 100644 --- a/app/html/src/client/preview/render.ts +++ b/app/html/src/client/preview/render.ts @@ -12,20 +12,21 @@ export default function renderMain({ showMain, showError, forceRender, + targetDOMNode = rootElement, }: RenderContext) { const element = storyFn(); showMain(); if (typeof element === 'string') { - rootElement.innerHTML = element; - simulatePageLoad(rootElement); + targetDOMNode.innerHTML = element; + simulatePageLoad(targetDOMNode); } else if (element instanceof Node) { // Don't re-mount the element if it didn't change and neither did the story - if (rootElement.firstChild === element && forceRender === true) { + if (targetDOMNode.firstChild === element && forceRender === true) { return; } - rootElement.innerHTML = ''; - rootElement.appendChild(element); + targetDOMNode.innerHTML = ''; + targetDOMNode.appendChild(element); simulateDOMContentLoaded(); } else { showError({ diff --git a/app/lit/src/client/preview/render.ts b/app/lit/src/client/preview/render.ts index 5dbee292df9c..22af7983f98d 100644 --- a/app/lit/src/client/preview/render.ts +++ b/app/lit/src/client/preview/render.ts @@ -6,13 +6,20 @@ import { RenderContext } from './types'; const rootElement = document.getElementById('root'); -export default function renderMain({ storyFn, kind, name, showMain, showError }: RenderContext) { +export default function renderMain({ + storyFn, + kind, + name, + showMain, + showError, + targetDOMNode = rootElement, +}: RenderContext) { const element = storyFn(); showMain(); - if (isTemplateResult(element)) { - render(element, rootElement); + if (isTemplateResult(element) && targetDOMNode) { + render(element, targetDOMNode); } else { showError({ title: `Expecting an lit template result from the story: "${name}" of "${kind}".`, diff --git a/app/preact/src/client/preview/render.tsx b/app/preact/src/client/preview/render.tsx index 1db691b3d7cb..944d8430584b 100644 --- a/app/preact/src/client/preview/render.tsx +++ b/app/preact/src/client/preview/render.tsx @@ -7,12 +7,12 @@ const rootElement = document ? document.getElementById('root') : null; let renderedStory: Element; -function preactRender(story: StoryFnPreactReturnType): void { +function preactRender(story: StoryFnPreactReturnType, targetDOMNode: HTMLElement): void { if (preact.Fragment) { // Preact 10 only: - preact.render(story, rootElement); + preact.render(story, targetDOMNode); } else { - renderedStory = (preact.render(story, rootElement, renderedStory) as unknown) as Element; + renderedStory = (preact.render(story, targetDOMNode, renderedStory) as unknown) as Element; } } @@ -43,13 +43,14 @@ export default function renderMain({ showMain, showError, forceRender, + targetDOMNode = rootElement, }: RenderContext) { // But forceRender means that it's the same story, so we want to keep the state in that case. if (!forceRender) { - preactRender(null); + preactRender(null, targetDOMNode); } showMain(); - preactRender(preact.h(StoryHarness, { name, kind, showError, storyFn })); + preactRender(preact.h(StoryHarness, { name, kind, showError, storyFn }), targetDOMNode); } diff --git a/app/server/src/client/preview/render.ts b/app/server/src/client/preview/render.ts index b3a38490ae75..d86ddebe1764 100644 --- a/app/server/src/client/preview/render.ts +++ b/app/server/src/client/preview/render.ts @@ -56,6 +56,7 @@ export async function renderMain({ storyFn, args, argTypes, + targetDOMNode = rootElement, }: RenderContext) { // Some addons wrap the storyFn so we need to call it even though Server doesn't need the answer storyFn(); @@ -71,16 +72,16 @@ export async function renderMain({ showMain(); if (typeof element === 'string') { - rootElement.innerHTML = element; - simulatePageLoad(rootElement); + targetDOMNode.innerHTML = element; + simulatePageLoad(targetDOMNode); } else if (element instanceof Node) { // Don't re-mount the element if it didn't change and neither did the story - if (rootElement.firstChild === element && forceRender === true) { + if (targetDOMNode.firstChild === element && forceRender === true) { return; } - rootElement.innerHTML = ''; - rootElement.appendChild(element); + targetDOMNode.innerHTML = ''; + targetDOMNode.appendChild(element); simulateDOMContentLoaded(); } else { showError({ diff --git a/app/svelte/src/client/preview/render.ts b/app/svelte/src/client/preview/render.ts index ba072e1400a6..70446d4b7c0a 100644 --- a/app/svelte/src/client/preview/render.ts +++ b/app/svelte/src/client/preview/render.ts @@ -5,6 +5,7 @@ import PreviewRender from './PreviewRender.svelte'; type Component = any; let previousComponent: Component = null; +const rootElement = document.getElementById('root'); function cleanUpPreviousStory() { if (!previousComponent) { @@ -14,15 +15,20 @@ function cleanUpPreviousStory() { previousComponent = null; } -export default function render({ storyFn, kind, name, showMain, showError }: RenderContext) { +export default function render({ + storyFn, + kind, + name, + showMain, + showError, + targetDOMNode = rootElement, +}: RenderContext) { cleanUpPreviousStory(); - const target = document.getElementById('root'); - - target.innerHTML = ''; + targetDOMNode.innerHTML = ''; previousComponent = new PreviewRender({ - target, + targetDOMNode, props: { storyFn, name, diff --git a/app/vue/src/client/preview/render.ts b/app/vue/src/client/preview/render.ts index 29c54bf00a28..ce70c326acc0 100644 --- a/app/vue/src/client/preview/render.ts +++ b/app/vue/src/client/preview/render.ts @@ -5,6 +5,8 @@ import { RenderContext } from './types'; export const COMPONENT = 'STORYBOOK_COMPONENT'; export const VALUES = 'STORYBOOK_VALUES'; +const rootElement = document.getElementById('root'); + const root = new Vue({ data() { return { @@ -27,6 +29,7 @@ export default function render({ showError, showException, forceRender, + targetDOMNode = rootElement, }: RenderContext) { Vue.config.errorHandler = showException; @@ -57,6 +60,6 @@ export default function render({ root[VALUES] = { ...element.options[VALUES], ...args }; if (!root.$el) { - root.$mount('#root'); + root.$mount(targetDOMNode); } } diff --git a/app/vue3/src/client/preview/render.ts b/app/vue3/src/client/preview/render.ts index 6a9cf7e1e648..ec8bf88dc7b3 100644 --- a/app/vue3/src/client/preview/render.ts +++ b/app/vue3/src/client/preview/render.ts @@ -3,6 +3,7 @@ import { createApp, h, shallowRef, ComponentPublicInstance } from 'vue'; import { RenderContext, StoryFnVueReturnType } from './types'; const activeStoryComponent = shallowRef(null); +const rootElement = document.getElementById('root'); let root: ComponentPublicInstance | null = null; @@ -30,6 +31,7 @@ export default function render({ showError, showException, forceRender, + targetDOMNode = rootElement, }: RenderContext) { storybookApp.config.errorHandler = showException; @@ -51,6 +53,6 @@ export default function render({ activeStoryComponent.value = element; if (!root) { - root = storybookApp.mount('#root'); + root = storybookApp.mount(targetDOMNode); } } diff --git a/app/web-components/src/client/preview/render.ts b/app/web-components/src/client/preview/render.ts index 5970b4d3e704..e293bd778a07 100644 --- a/app/web-components/src/client/preview/render.ts +++ b/app/web-components/src/client/preview/render.ts @@ -13,6 +13,7 @@ export default function renderMain({ showMain, showError, forceRender, + targetDOMNode = rootElement, }: RenderContext) { const element = storyFn(); @@ -22,23 +23,23 @@ export default function renderMain({ // Since we reuse `rootElement` for all stories, remove the stored instance first. // But forceRender means that it's the same story, so we want too keep the state in that case. if (!forceRender || !rootElement.querySelector('[id="root-inner"]')) { - rootElement.innerHTML = '
'; + targetDOMNode.innerHTML = '
'; } - const renderTo = rootElement.querySelector('[id="root-inner"]'); + const renderTo = targetDOMNode.querySelector('[id="root-inner"]'); render(element, renderTo); - simulatePageLoad(rootElement); + simulatePageLoad(targetDOMNode); } else if (typeof element === 'string') { - rootElement.innerHTML = element; - simulatePageLoad(rootElement); + targetDOMNode.innerHTML = element; + simulatePageLoad(targetDOMNode); } else if (element instanceof Node) { // Don't re-mount the element if it didn't change and neither did the story - if (rootElement.firstChild === element && forceRender === true) { + if (targetDOMNode.firstChild === element && forceRender === true) { return; } - rootElement.innerHTML = ''; - rootElement.appendChild(element); + targetDOMNode.innerHTML = ''; + targetDOMNode.appendChild(element); simulateDOMContentLoaded(); } else { showError({ From 5b59497e0e9ce9af0c3dafabd65cc404ce683ba8 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 12:29:46 +0200 Subject: [PATCH 04/16] FIX vue3 --- app/vue3/src/client/preview/render.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/app/vue3/src/client/preview/render.ts b/app/vue3/src/client/preview/render.ts index ec8bf88dc7b3..11ee7d5d361e 100644 --- a/app/vue3/src/client/preview/render.ts +++ b/app/vue3/src/client/preview/render.ts @@ -1,5 +1,5 @@ import dedent from 'ts-dedent'; -import { createApp, h, shallowRef, ComponentPublicInstance } from 'vue'; +import { createApp, h, shallowRef, ComponentPublicInstance, render } from 'vue'; import { RenderContext, StoryFnVueReturnType } from './types'; const activeStoryComponent = shallowRef(null); @@ -22,7 +22,7 @@ export const storybookApp = createApp({ }, }); -export default function render({ +export default function renderMain({ storyFn, kind, name, @@ -48,11 +48,21 @@ export default function render({ return; } - showMain(); + if (!root) { + activeStoryComponent.value = element; + root = storybookApp.mount(`#root`); + } - activeStoryComponent.value = element; + if (targetDOMNode.id === 'root') { + activeStoryComponent.value = element; + } else { + const vnode = h(element, args); + // By attaching the app context from `@storybook/vue3` to the vnode + // like this, these stoeis are able to access any app config stuff + // the end-user set inside `.storybook/preview.js` + vnode.appContext = storybookApp._context; // eslint-disable-line no-underscore-dangle - if (!root) { - root = storybookApp.mount(targetDOMNode); + targetDOMNode.innerHTML = ''; + render(vnode, targetDOMNode); } } From e432d108c7e04b051d389cf2bbdbac52f234db8b Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 13:09:49 +0200 Subject: [PATCH 05/16] fix lit used a pretty hacky way to remove the loading indicator in lit --- app/lit/src/client/preview/render.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/lit/src/client/preview/render.ts b/app/lit/src/client/preview/render.ts index 22af7983f98d..534226725c64 100644 --- a/app/lit/src/client/preview/render.ts +++ b/app/lit/src/client/preview/render.ts @@ -10,13 +10,23 @@ export default function renderMain({ storyFn, kind, name, - showMain, showError, targetDOMNode = rootElement, }: RenderContext) { const element = storyFn(); - showMain(); + if (targetDOMNode === null) { + return; + } + + // in docs-mode we initially render a placeholder, lit will NOT replace it, but rather render BELOW + // We clear out the loading-indicator, lit however will not render a second time if we cleared the contents. + // So we detect if our loading-indicator exists, and if it does, we clear the contents + // nodeType 8 is a html-comment, which is what lit injects, so I use that to detect if lit has rendered + if (targetDOMNode && targetDOMNode.id !== 'root' && targetDOMNode.firstChild?.nodeType !== 8) { + // eslint-disable-next-line no-param-reassign + targetDOMNode.innerHTML = ''; + } if (isTemplateResult(element) && targetDOMNode) { render(element, targetDOMNode); From 2c2b25d26225a237f20bcab22fcc758537f87a1c Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 13:09:59 +0200 Subject: [PATCH 06/16] cleanup render in react --- app/react/src/client/preview/render.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/react/src/client/preview/render.tsx b/app/react/src/client/preview/render.tsx index 95084450521c..6265abc09a6b 100644 --- a/app/react/src/client/preview/render.tsx +++ b/app/react/src/client/preview/render.tsx @@ -69,15 +69,11 @@ export default async function renderMain({ // This could leads to issues like below: // https://github.com/storybookjs/react-storybook/issues/81 // But forceRender means that it's the same story, so we want too keep the state in that case. - if (!targetDOMNode) { - debugger; - } - if (!forceRender) { try { ReactDOM.unmountComponentAtNode(targetDOMNode); } catch (e) { - debugger; + // } } From c63460d66933cad409f95a2ca353fc0df2b41c1c Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 13:10:22 +0200 Subject: [PATCH 07/16] remove scrollbar in toolbar in docs (should extract into separate PR) --- lib/components/src/bar/button.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/components/src/bar/button.tsx b/lib/components/src/bar/button.tsx index b6474d53b849..a79b7afce8c6 100644 --- a/lib/components/src/bar/button.tsx +++ b/lib/components/src/bar/button.tsx @@ -84,6 +84,7 @@ export const IconButton = styled(ButtonOrLink, { shouldForwardProp: isPropValid color: 'inherit', padding: 0, cursor: 'pointer', + boxSizing: 'border-box', // Icon Buttons may have text depending on user preferences. // While we don't recommend having text for IconButtons, this style ensures that the text is the correct size. From 3ed43fc3079f3000d42089c94f7a01918549bb12 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 13:10:42 +0200 Subject: [PATCH 08/16] fix lockfile for litexample --- examples/lit-kitchen-sink/yarn.lock | 292 ++++++++++++++-------------- 1 file changed, 144 insertions(+), 148 deletions(-) diff --git a/examples/lit-kitchen-sink/yarn.lock b/examples/lit-kitchen-sink/yarn.lock index fee18f26a659..f9d78c33077d 100644 --- a/examples/lit-kitchen-sink/yarn.lock +++ b/examples/lit-kitchen-sink/yarn.lock @@ -1763,14 +1763,14 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-a11y@portal:../../addons/a11y::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/channels": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/channels": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 + "@storybook/theming": 6.3.0-alpha.25 axe-core: ^4.2.0 core-js: ^3.8.2 global: ^4.4.0 @@ -1794,12 +1794,12 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-actions@portal:../../addons/actions::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 + "@storybook/theming": 6.3.0-alpha.25 core-js: ^3.8.2 fast-deep-equal: ^3.1.3 global: ^4.4.0 @@ -1826,12 +1826,12 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-backgrounds@portal:../../addons/backgrounds::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 + "@storybook/theming": 6.3.0-alpha.25 core-js: ^3.8.2 global: ^4.4.0 memoizerific: ^1.11.3 @@ -1853,12 +1853,12 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-controls@portal:../../addons/controls::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/node-logger": 6.3.0-alpha.22 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/node-logger": 6.3.0-alpha.25 + "@storybook/theming": 6.3.0-alpha.25 core-js: ^3.8.2 ts-dedent: ^2.0.0 peerDependencies: @@ -1885,19 +1885,19 @@ __metadata: "@mdx-js/loader": ^1.6.22 "@mdx-js/mdx": ^1.6.22 "@mdx-js/react": ^1.6.22 - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/builder-webpack4": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/core": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/builder-webpack4": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/core": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 "@storybook/csf": 0.0.1 - "@storybook/node-logger": 6.3.0-alpha.22 - "@storybook/postinstall": 6.3.0-alpha.22 - "@storybook/source-loader": 6.3.0-alpha.22 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/node-logger": 6.3.0-alpha.25 + "@storybook/postinstall": 6.3.0-alpha.25 + "@storybook/source-loader": 6.3.0-alpha.25 + "@storybook/theming": 6.3.0-alpha.25 acorn: ^7.4.1 acorn-jsx: ^5.3.1 acorn-walk: ^7.2.0 @@ -1919,11 +1919,11 @@ __metadata: ts-dedent: ^2.0.0 util-deprecate: ^1.0.2 peerDependencies: - "@storybook/angular": 6.3.0-alpha.22 - "@storybook/lit": 6.3.0-alpha.22 - "@storybook/vue": 6.3.0-alpha.22 - "@storybook/vue3": 6.3.0-alpha.22 - "@storybook/web-components": 6.3.0-alpha.22 + "@storybook/angular": 6.3.0-alpha.25 + "@storybook/lit": 6.3.0-alpha.25 + "@storybook/vue": 6.3.0-alpha.25 + "@storybook/vue3": 6.3.0-alpha.25 + "@storybook/web-components": 6.3.0-alpha.25 lit: ^2.0.0-rc.1 lit-html: ^1.0.0 react: ^16.8.0 || ^17.0.0 @@ -1966,11 +1966,11 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-links@portal:../../addons/links::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 "@storybook/csf": 0.0.1 - "@storybook/router": 6.3.0-alpha.22 + "@storybook/router": 6.3.0-alpha.25 "@types/qs": ^6.9.5 core-js: ^3.8.2 global: ^4.4.0 @@ -1993,13 +1993,13 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-storysource@portal:../../addons/storysource::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/router": 6.3.0-alpha.22 - "@storybook/source-loader": 6.3.0-alpha.22 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/router": 6.3.0-alpha.25 + "@storybook/source-loader": 6.3.0-alpha.25 + "@storybook/theming": 6.3.0-alpha.25 core-js: ^3.8.2 estraverse: ^5.2.0 loader-utils: ^2.0.0 @@ -2022,10 +2022,10 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-toolbars@portal:../../addons/toolbars::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 core-js: ^3.8.2 peerDependencies: react: ^16.8.0 || ^17.0.0 @@ -2042,12 +2042,12 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-viewport@portal:../../addons/viewport::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 + "@storybook/theming": 6.3.0-alpha.25 core-js: ^3.8.2 global: ^4.4.0 memoizerific: ^1.11.3 @@ -2068,12 +2068,12 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addons@portal:../../lib/addons::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/api": 6.3.0-alpha.22 - "@storybook/channels": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 - "@storybook/router": 6.3.0-alpha.22 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/channels": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 + "@storybook/router": 6.3.0-alpha.25 + "@storybook/theming": 6.3.0-alpha.25 core-js: ^3.8.2 global: ^4.4.0 regenerator-runtime: ^0.13.7 @@ -2088,13 +2088,13 @@ __metadata: resolution: "@storybook/api@portal:../../lib/api::locator=lit-kitchen-sink%40workspace%3A." dependencies: "@reach/router": ^1.3.4 - "@storybook/channels": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 + "@storybook/channels": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 "@storybook/csf": 0.0.1 - "@storybook/router": 6.3.0-alpha.22 + "@storybook/router": 6.3.0-alpha.25 "@storybook/semver": ^7.3.2 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/theming": 6.3.0-alpha.25 "@types/reach__router": ^1.3.7 core-js: ^3.8.2 fast-deep-equal: ^3.1.3 @@ -2138,20 +2138,20 @@ __metadata: "@babel/preset-env": ^7.12.11 "@babel/preset-react": ^7.12.10 "@babel/preset-typescript": ^7.12.7 - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/channel-postmessage": 6.3.0-alpha.22 - "@storybook/channels": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/core-common": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 - "@storybook/node-logger": 6.3.0-alpha.22 - "@storybook/router": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/channel-postmessage": 6.3.0-alpha.25 + "@storybook/channels": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/core-common": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 + "@storybook/node-logger": 6.3.0-alpha.25 + "@storybook/router": 6.3.0-alpha.25 "@storybook/semver": ^7.3.2 - "@storybook/theming": 6.3.0-alpha.22 - "@storybook/ui": 6.3.0-alpha.22 + "@storybook/theming": 6.3.0-alpha.25 + "@storybook/ui": 6.3.0-alpha.25 "@types/node": ^14.0.10 "@types/webpack": ^4.41.26 autoprefixer: ^9.8.6 @@ -2220,19 +2220,19 @@ __metadata: "@babel/preset-env": ^7.12.11 "@babel/preset-react": ^7.12.10 "@babel/preset-typescript": ^7.12.7 - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/channel-postmessage": 6.3.0-alpha.22 - "@storybook/channels": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/core-common": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 - "@storybook/node-logger": 6.3.0-alpha.22 - "@storybook/router": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/channel-postmessage": 6.3.0-alpha.25 + "@storybook/channels": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/core-common": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 + "@storybook/node-logger": 6.3.0-alpha.25 + "@storybook/router": 6.3.0-alpha.25 "@storybook/semver": ^7.3.2 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/theming": 6.3.0-alpha.25 "@types/node": ^14.0.10 babel-loader: ^8.2.2 babel-plugin-macros: ^3.0.1 @@ -2241,20 +2241,16 @@ __metadata: core-js: ^3.8.2 css-loader: ^5.0.1 dotenv-webpack: ^6.0.0 - file-loader: ^6.2.0 fork-ts-checker-webpack-plugin: ^6.0.4 fs-extra: ^9.0.1 glob: ^7.1.6 glob-promise: ^3.4.0 html-webpack-plugin: ^5.0.0 - pnp-webpack-plugin: 1.6.4 - raw-loader: ^4.0.2 react-dev-utils: ^11.0.3 stable: ^0.1.8 style-loader: ^2.0.0 terser-webpack-plugin: ^5.0.3 ts-dedent: ^2.0.0 - url-loader: ^4.1.1 util-deprecate: ^1.0.2 webpack: ^5.9.0 webpack-dev-middleware: ^4.1.0 @@ -2273,9 +2269,9 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/channel-postmessage@portal:../../lib/channel-postmessage::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/channels": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 + "@storybook/channels": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 core-js: ^3.8.2 global: ^4.4.0 qs: ^6.10.0 @@ -2297,11 +2293,11 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/client-api@portal:../../lib/client-api::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/channel-postmessage": 6.3.0-alpha.22 - "@storybook/channels": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/channel-postmessage": 6.3.0-alpha.25 + "@storybook/channels": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 "@storybook/csf": 0.0.1 "@types/qs": ^6.9.5 "@types/webpack-env": ^1.16.0 @@ -2335,9 +2331,9 @@ __metadata: resolution: "@storybook/components@portal:../../lib/components::locator=lit-kitchen-sink%40workspace%3A." dependencies: "@popperjs/core": ^2.6.0 - "@storybook/client-logger": 6.3.0-alpha.22 + "@storybook/client-logger": 6.3.0-alpha.25 "@storybook/csf": 0.0.1 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/theming": 6.3.0-alpha.25 "@types/color-convert": ^2.0.0 "@types/overlayscrollbars": ^1.12.0 "@types/react-syntax-highlighter": 11.0.5 @@ -2368,13 +2364,13 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/core-client@portal:../../lib/core-client::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/channel-postmessage": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/channel-postmessage": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 "@storybook/csf": 0.0.1 - "@storybook/ui": 6.3.0-alpha.22 + "@storybook/ui": 6.3.0-alpha.25 ansi-to-html: ^0.6.11 core-js: ^3.8.2 global: ^4.4.0 @@ -2419,7 +2415,7 @@ __metadata: "@babel/preset-react": ^7.12.10 "@babel/preset-typescript": ^7.12.7 "@babel/register": ^7.12.1 - "@storybook/node-logger": 6.3.0-alpha.22 + "@storybook/node-logger": 6.3.0-alpha.25 "@storybook/semver": ^7.3.2 "@types/glob-base": ^0.3.0 "@types/micromatch": ^4.0.1 @@ -2470,14 +2466,14 @@ __metadata: "@babel/core": ^7.12.10 "@babel/plugin-transform-template-literals": ^7.12.1 "@babel/preset-react": ^7.12.10 - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/builder-webpack4": 6.3.0-alpha.22 - "@storybook/core-client": 6.3.0-alpha.22 - "@storybook/core-common": 6.3.0-alpha.22 - "@storybook/node-logger": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/builder-webpack4": 6.3.0-alpha.25 + "@storybook/core-client": 6.3.0-alpha.25 + "@storybook/core-common": 6.3.0-alpha.25 + "@storybook/node-logger": 6.3.0-alpha.25 "@storybook/semver": ^7.3.2 - "@storybook/theming": 6.3.0-alpha.22 - "@storybook/ui": 6.3.0-alpha.22 + "@storybook/theming": 6.3.0-alpha.25 + "@storybook/ui": 6.3.0-alpha.25 "@types/node": ^14.0.10 "@types/node-fetch": ^2.5.7 "@types/pretty-hrtime": ^1.0.0 @@ -2522,7 +2518,7 @@ __metadata: webpack-dev-middleware: ^3.7.3 webpack-virtual-modules: ^0.2.2 peerDependencies: - "@storybook/builder-webpack5": 6.3.0-alpha.22 + "@storybook/builder-webpack5": 6.3.0-alpha.25 react: ^16.8.0 || ^17.0.0 react-dom: ^16.8.0 || ^17.0.0 peerDependenciesMeta: @@ -2537,10 +2533,10 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/core@portal:../../lib/core::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/core-client": 6.3.0-alpha.22 - "@storybook/core-server": 6.3.0-alpha.22 + "@storybook/core-client": 6.3.0-alpha.25 + "@storybook/core-server": 6.3.0-alpha.25 peerDependencies: - "@storybook/builder-webpack5": 6.3.0-alpha.22 + "@storybook/builder-webpack5": 6.3.0-alpha.25 react: ^16.8.0 || ^17.0.0 react-dom: ^16.8.0 || ^17.0.0 peerDependenciesMeta: @@ -2564,10 +2560,10 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/lit@portal:../../app/lit::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/client-api": 6.3.0-alpha.22 - "@storybook/core": 6.3.0-alpha.22 - "@storybook/core-common": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/client-api": 6.3.0-alpha.25 + "@storybook/core": 6.3.0-alpha.25 + "@storybook/core-common": 6.3.0-alpha.25 "@types/webpack-env": ^1.16.0 core-js: ^3.8.2 global: ^4.4.0 @@ -2609,7 +2605,7 @@ __metadata: resolution: "@storybook/router@portal:../../lib/router::locator=lit-kitchen-sink%40workspace%3A." dependencies: "@reach/router": ^1.3.4 - "@storybook/client-logger": 6.3.0-alpha.22 + "@storybook/client-logger": 6.3.0-alpha.25 "@types/reach__router": ^1.3.7 core-js: ^3.8.2 fast-deep-equal: ^3.1.3 @@ -2640,8 +2636,8 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/source-loader@portal:../../lib/source-loader::locator=lit-kitchen-sink%40workspace%3A." dependencies: - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 "@storybook/csf": 0.0.1 core-js: ^3.8.2 estraverse: ^5.2.0 @@ -2663,7 +2659,7 @@ __metadata: "@emotion/core": ^10.1.1 "@emotion/is-prop-valid": ^0.8.6 "@emotion/styled": ^10.0.27 - "@storybook/client-logger": 6.3.0-alpha.22 + "@storybook/client-logger": 6.3.0-alpha.25 core-js: ^3.8.2 deep-object-diff: ^1.1.0 emotion-theming: ^10.0.27 @@ -2683,15 +2679,15 @@ __metadata: resolution: "@storybook/ui@portal:../../lib/ui::locator=lit-kitchen-sink%40workspace%3A." dependencies: "@emotion/core": ^10.1.1 - "@storybook/addons": 6.3.0-alpha.22 - "@storybook/api": 6.3.0-alpha.22 - "@storybook/channels": 6.3.0-alpha.22 - "@storybook/client-logger": 6.3.0-alpha.22 - "@storybook/components": 6.3.0-alpha.22 - "@storybook/core-events": 6.3.0-alpha.22 - "@storybook/router": 6.3.0-alpha.22 + "@storybook/addons": 6.3.0-alpha.25 + "@storybook/api": 6.3.0-alpha.25 + "@storybook/channels": 6.3.0-alpha.25 + "@storybook/client-logger": 6.3.0-alpha.25 + "@storybook/components": 6.3.0-alpha.25 + "@storybook/core-events": 6.3.0-alpha.25 + "@storybook/router": 6.3.0-alpha.25 "@storybook/semver": ^7.3.2 - "@storybook/theming": 6.3.0-alpha.22 + "@storybook/theming": 6.3.0-alpha.25 "@types/markdown-to-jsx": ^6.11.3 copy-to-clipboard: ^3.3.1 core-js: ^3.8.2 From 1ed43aaa263e67394c8b4500bf6b3e1ce30cc1b2 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 14:30:44 +0200 Subject: [PATCH 09/16] ADD a selectable identifier on the loading indicator so we can remove it when the story is about to get rendered into it This is much better then trying to replace-render in every framework. --- addons/docs/src/blocks/Story.tsx | 2 +- app/lit/src/client/preview/render.ts | 9 --------- app/preact/src/client/preview/render.tsx | 3 --- lib/core-client/src/preview/StoryRenderer.tsx | 4 ++++ 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/addons/docs/src/blocks/Story.tsx b/addons/docs/src/blocks/Story.tsx index 83175d85d7c8..319519973b55 100644 --- a/addons/docs/src/blocks/Story.tsx +++ b/addons/docs/src/blocks/Story.tsx @@ -53,7 +53,7 @@ const Placeholder: FunctionComponent = ({ id, name }) => { return (
- loading story... + loading story...
); }; diff --git a/app/lit/src/client/preview/render.ts b/app/lit/src/client/preview/render.ts index 534226725c64..dd39da0843ec 100644 --- a/app/lit/src/client/preview/render.ts +++ b/app/lit/src/client/preview/render.ts @@ -19,15 +19,6 @@ export default function renderMain({ return; } - // in docs-mode we initially render a placeholder, lit will NOT replace it, but rather render BELOW - // We clear out the loading-indicator, lit however will not render a second time if we cleared the contents. - // So we detect if our loading-indicator exists, and if it does, we clear the contents - // nodeType 8 is a html-comment, which is what lit injects, so I use that to detect if lit has rendered - if (targetDOMNode && targetDOMNode.id !== 'root' && targetDOMNode.firstChild?.nodeType !== 8) { - // eslint-disable-next-line no-param-reassign - targetDOMNode.innerHTML = ''; - } - if (isTemplateResult(element) && targetDOMNode) { render(element, targetDOMNode); } else { diff --git a/app/preact/src/client/preview/render.tsx b/app/preact/src/client/preview/render.tsx index 944d8430584b..2dec35bf1cb5 100644 --- a/app/preact/src/client/preview/render.tsx +++ b/app/preact/src/client/preview/render.tsx @@ -40,7 +40,6 @@ export default function renderMain({ storyFn, kind, name, - showMain, showError, forceRender, targetDOMNode = rootElement, @@ -50,7 +49,5 @@ export default function renderMain({ preactRender(null, targetDOMNode); } - showMain(); - preactRender(preact.h(StoryHarness, { name, kind, showError, storyFn }), targetDOMNode); } diff --git a/lib/core-client/src/preview/StoryRenderer.tsx b/lib/core-client/src/preview/StoryRenderer.tsx index 3828afbc56d8..2c60cb08294d 100644 --- a/lib/core-client/src/preview/StoryRenderer.tsx +++ b/lib/core-client/src/preview/StoryRenderer.tsx @@ -123,6 +123,10 @@ export class StoryRenderer { console.timeEnd(`applyLoaders_${id}`); const storyFn = () => unboundStoryFn(storyContext); + if (targetDOMNode.querySelector('[data-is-loadering-indicator="true"')) { + targetDOMNode.querySelector('[data-is-loadering-indicator="true"').remove(); + } + console.time(`render_${id}`); await this.render({ ...context, From 600bcee37abd79e353c92e76f64b9c132bac767a Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 15:05:25 +0200 Subject: [PATCH 10/16] attempt to fix svelte, but failed --- app/svelte/src/client/preview/render.ts | 42 ++++++++++++++++--------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/app/svelte/src/client/preview/render.ts b/app/svelte/src/client/preview/render.ts index 70446d4b7c0a..aef99eaacafb 100644 --- a/app/svelte/src/client/preview/render.ts +++ b/app/svelte/src/client/preview/render.ts @@ -11,7 +11,11 @@ function cleanUpPreviousStory() { if (!previousComponent) { return; } - previousComponent.$destroy(); + try { + previousComponent.$destroy(); + } catch (e) { + // + } previousComponent = null; } @@ -19,23 +23,33 @@ export default function render({ storyFn, kind, name, - showMain, showError, targetDOMNode = rootElement, }: RenderContext) { cleanUpPreviousStory(); - targetDOMNode.innerHTML = ''; - - previousComponent = new PreviewRender({ - targetDOMNode, - props: { - storyFn, - name, - kind, - showError, - }, - }); + // targetDOMNode.innerHTML = ''; - showMain(); + if (targetDOMNode === rootElement) { + previousComponent = new PreviewRender({ + targetDOMNode, + props: { + storyFn, + name, + kind, + showError, + }, + }); + } else { + // eslint-disable-next-line no-new + new PreviewRender({ + targetDOMNode, + props: { + storyFn, + name, + kind, + showError, + }, + }); + } } From c62fd4a3faa8d35e976028b2696173c9f8fa90f5 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 15:22:22 +0200 Subject: [PATCH 11/16] fix vue2 rendering, but controls do not yet work correctly in docs --- app/vue/src/client/preview/render.ts | 42 ++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/app/vue/src/client/preview/render.ts b/app/vue/src/client/preview/render.ts index ce70c326acc0..0f46bfc11f37 100644 --- a/app/vue/src/client/preview/render.ts +++ b/app/vue/src/client/preview/render.ts @@ -25,7 +25,6 @@ export default function render({ kind, name, args, - showMain, showError, showException, forceRender, @@ -49,17 +48,42 @@ export default function render({ return; } - showMain(); + if (!root.$el) { + // at component creation || refresh by HMR or switching stories + if (!root[COMPONENT] || !forceRender) { + root[COMPONENT] = element; + } + + // @ts-ignore https://github.com/storybookjs/storrybook/pull/7578#discussion_r307986139 + root[VALUES] = { ...element.options[VALUES], ...args }; - // at component creation || refresh by HMR or switching stories - if (!root[COMPONENT] || !forceRender) { - root[COMPONENT] = element; + root.$mount(rootElement); } - // @ts-ignore https://github.com/storybookjs/storrybook/pull/7578#discussion_r307986139 - root[VALUES] = { ...element.options[VALUES], ...args }; + if (targetDOMNode.id === 'root') { + // at component creation || refresh by HMR or switching stories + if (!root[COMPONENT] || !forceRender) { + root[COMPONENT] = element; + } - if (!root.$el) { - root.$mount(targetDOMNode); + // @ts-ignore https://github.com/storybookjs/storrybook/pull/7578#discussion_r307986139 + root[VALUES] = { ...element.options[VALUES], ...args }; + } else { + const component = storyFn(); + + // eslint-disable-next-line no-new + new Vue({ + el: targetDOMNode, + data() { + return { + [COMPONENT]: component, + [VALUES]: args, + }; + }, + render(h) { + const children = this[COMPONENT] ? [h(this[COMPONENT])] : undefined; + return h('div', { attrs: { id: 'root' } }, children); + }, + }); } } From 00f41ce4ac4571380772d3e102bb1f8810afc770 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 16:16:16 +0200 Subject: [PATCH 12/16] make the renderMain function the same name in all frameworks --- app/angular/src/client/preview/render.ts | 8 ++++---- app/svelte/src/client/preview/render.ts | 2 +- app/vue/src/client/preview/render.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/angular/src/client/preview/render.ts b/app/angular/src/client/preview/render.ts index fcd1e0b2b557..5cc811514ca2 100644 --- a/app/angular/src/client/preview/render.ts +++ b/app/angular/src/client/preview/render.ts @@ -5,20 +5,20 @@ import { renderNgApp } from './angular/helpers'; import { StoryFnAngularReturnType } from './types'; import { Parameters } from './types-6-0'; +const rootElement = document ? document.getElementById('root') : null; + // add proper types -export default function render({ +export default function renderMain({ storyFn, - showMain, forceRender, parameters, + targetDOMNode = rootElement, }: { storyFn: StoryFn; showMain: () => void; forceRender: boolean; parameters: Parameters; }) { - showMain(); - if (parameters.angularLegacyRendering) { renderNgApp(storyFn, forceRender); return; diff --git a/app/svelte/src/client/preview/render.ts b/app/svelte/src/client/preview/render.ts index aef99eaacafb..32609c2860e7 100644 --- a/app/svelte/src/client/preview/render.ts +++ b/app/svelte/src/client/preview/render.ts @@ -19,7 +19,7 @@ function cleanUpPreviousStory() { previousComponent = null; } -export default function render({ +export default function renderMain({ storyFn, kind, name, diff --git a/app/vue/src/client/preview/render.ts b/app/vue/src/client/preview/render.ts index 0f46bfc11f37..92ddaaf3a2d1 100644 --- a/app/vue/src/client/preview/render.ts +++ b/app/vue/src/client/preview/render.ts @@ -5,7 +5,7 @@ import { RenderContext } from './types'; export const COMPONENT = 'STORYBOOK_COMPONENT'; export const VALUES = 'STORYBOOK_VALUES'; -const rootElement = document.getElementById('root'); +const rootElement = global.document.getElementById('root'); const root = new Vue({ data() { @@ -20,7 +20,7 @@ const root = new Vue({ }, }); -export default function render({ +export default function renderMain({ storyFn, kind, name, From ceff50346bf131e5ebcabc1e3787c3f3ae788f11 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 16:22:27 +0200 Subject: [PATCH 13/16] fix linting --- app/ember/src/client/preview/render.ts | 2 -- app/html/src/client/preview/render.ts | 2 -- app/server/src/client/preview/render.ts | 2 -- app/vue3/src/client/preview/render.ts | 1 - app/web-components/src/client/preview/render.ts | 2 -- 5 files changed, 9 deletions(-) diff --git a/app/ember/src/client/preview/render.ts b/app/ember/src/client/preview/render.ts index 73f6fecbebae..fa1a0336b1e4 100644 --- a/app/ember/src/client/preview/render.ts +++ b/app/ember/src/client/preview/render.ts @@ -61,7 +61,6 @@ export default function renderMain({ storyFn, kind, name, - showMain, showError, targetDOMNode = rootElement, }: RenderContext) { @@ -80,6 +79,5 @@ export default function renderMain({ return; } - showMain(); render(element, { el: targetDOMNode }); } diff --git a/app/html/src/client/preview/render.ts b/app/html/src/client/preview/render.ts index 09e2e55d8a79..9b3155ad3340 100644 --- a/app/html/src/client/preview/render.ts +++ b/app/html/src/client/preview/render.ts @@ -9,13 +9,11 @@ export default function renderMain({ storyFn, kind, name, - showMain, showError, forceRender, targetDOMNode = rootElement, }: RenderContext) { const element = storyFn(); - showMain(); if (typeof element === 'string') { targetDOMNode.innerHTML = element; simulatePageLoad(targetDOMNode); diff --git a/app/server/src/client/preview/render.ts b/app/server/src/client/preview/render.ts index d86ddebe1764..a21bf70def62 100644 --- a/app/server/src/client/preview/render.ts +++ b/app/server/src/client/preview/render.ts @@ -48,7 +48,6 @@ export async function renderMain({ id, kind, name, - showMain, showError, forceRender, parameters, @@ -70,7 +69,6 @@ export async function renderMain({ const storyParams = { ...params, ...storyArgs }; const element = await fetchStoryHtml(url, fetchId, storyParams, storyContext); - showMain(); if (typeof element === 'string') { targetDOMNode.innerHTML = element; simulatePageLoad(targetDOMNode); diff --git a/app/vue3/src/client/preview/render.ts b/app/vue3/src/client/preview/render.ts index 11ee7d5d361e..6a53acdc42bb 100644 --- a/app/vue3/src/client/preview/render.ts +++ b/app/vue3/src/client/preview/render.ts @@ -27,7 +27,6 @@ export default function renderMain({ kind, name, args, - showMain, showError, showException, forceRender, diff --git a/app/web-components/src/client/preview/render.ts b/app/web-components/src/client/preview/render.ts index e293bd778a07..aa1d27fa780a 100644 --- a/app/web-components/src/client/preview/render.ts +++ b/app/web-components/src/client/preview/render.ts @@ -10,14 +10,12 @@ export default function renderMain({ storyFn, kind, name, - showMain, showError, forceRender, targetDOMNode = rootElement, }: RenderContext) { const element = storyFn(); - showMain(); if (element instanceof TemplateResult) { // `render` stores the TemplateInstance in the Node and tries to update based on that. // Since we reuse `rootElement` for all stories, remove the stored instance first. From 3b7969592a8032685c903f6bfec077779696fdd6 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 17:05:11 +0200 Subject: [PATCH 14/16] FIX linting & ts --- app/angular/src/client/preview/render.ts | 3 ++- app/html/src/client/preview/render.ts | 1 + app/server/src/client/preview/render.ts | 1 + app/vue3/src/client/preview/render.ts | 1 + app/web-components/src/client/preview/render.ts | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/angular/src/client/preview/render.ts b/app/angular/src/client/preview/render.ts index 5cc811514ca2..9101d32db43d 100644 --- a/app/angular/src/client/preview/render.ts +++ b/app/angular/src/client/preview/render.ts @@ -5,7 +5,7 @@ import { renderNgApp } from './angular/helpers'; import { StoryFnAngularReturnType } from './types'; import { Parameters } from './types-6-0'; -const rootElement = document ? document.getElementById('root') : null; +const rootElement = global.document.getElementById('root'); // add proper types export default function renderMain({ @@ -18,6 +18,7 @@ export default function renderMain({ showMain: () => void; forceRender: boolean; parameters: Parameters; + targetDOMNode: HTMLElement; }) { if (parameters.angularLegacyRendering) { renderNgApp(storyFn, forceRender); diff --git a/app/html/src/client/preview/render.ts b/app/html/src/client/preview/render.ts index 9b3155ad3340..776fcae94fdf 100644 --- a/app/html/src/client/preview/render.ts +++ b/app/html/src/client/preview/render.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ import { document, Node } from 'global'; import dedent from 'ts-dedent'; import { simulatePageLoad, simulateDOMContentLoaded } from '@storybook/client-api'; diff --git a/app/server/src/client/preview/render.ts b/app/server/src/client/preview/render.ts index a21bf70def62..bb418c7ad43d 100644 --- a/app/server/src/client/preview/render.ts +++ b/app/server/src/client/preview/render.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ import { document, fetch, Node } from 'global'; import dedent from 'ts-dedent'; import { Args, ArgTypes } from '@storybook/api'; diff --git a/app/vue3/src/client/preview/render.ts b/app/vue3/src/client/preview/render.ts index 6a53acdc42bb..d011dd3f8bd8 100644 --- a/app/vue3/src/client/preview/render.ts +++ b/app/vue3/src/client/preview/render.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ import dedent from 'ts-dedent'; import { createApp, h, shallowRef, ComponentPublicInstance, render } from 'vue'; import { RenderContext, StoryFnVueReturnType } from './types'; diff --git a/app/web-components/src/client/preview/render.ts b/app/web-components/src/client/preview/render.ts index aa1d27fa780a..be7a9efc32c0 100644 --- a/app/web-components/src/client/preview/render.ts +++ b/app/web-components/src/client/preview/render.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ import { document, Node } from 'global'; import dedent from 'ts-dedent'; import { render, TemplateResult } from 'lit-html'; From 79e222f3e494f54410cb9f95513d0927d7392e82 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 14 May 2021 17:11:40 +0200 Subject: [PATCH 15/16] fix linting --- app/vue3/src/client/preview/render.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/vue3/src/client/preview/render.ts b/app/vue3/src/client/preview/render.ts index d011dd3f8bd8..b2d9dfbcfe18 100644 --- a/app/vue3/src/client/preview/render.ts +++ b/app/vue3/src/client/preview/render.ts @@ -4,7 +4,7 @@ import { createApp, h, shallowRef, ComponentPublicInstance, render } from 'vue'; import { RenderContext, StoryFnVueReturnType } from './types'; const activeStoryComponent = shallowRef(null); -const rootElement = document.getElementById('root'); +const rootElement = global.document.getElementById('root'); let root: ComponentPublicInstance | null = null; From cdf70bde84ebc968b3dfcea706bfc8e47478358c Mon Sep 17 00:00:00 2001 From: Michael Shilman Date: Fri, 25 Jun 2021 18:25:00 +0800 Subject: [PATCH 16/16] Add a feature flag for modern inline rendering --- addons/docs/src/blocks/LegacyStory.tsx | 102 ++++++++++++++++++ addons/docs/src/blocks/ModernStory.tsx | 83 ++++++++++++++ addons/docs/src/blocks/Story.tsx | 87 ++------------- examples/react-ts/main.ts | 1 + .../src/preview/iframe-webpack.config.ts | 2 + .../src/preview/iframe-webpack.config.ts | 4 + lib/core-common/src/types.ts | 6 ++ lib/core-server/src/build-dev.ts | 1 + lib/core-server/src/build-static.ts | 1 + 9 files changed, 206 insertions(+), 81 deletions(-) create mode 100644 addons/docs/src/blocks/LegacyStory.tsx create mode 100644 addons/docs/src/blocks/ModernStory.tsx diff --git a/addons/docs/src/blocks/LegacyStory.tsx b/addons/docs/src/blocks/LegacyStory.tsx new file mode 100644 index 000000000000..ed4ecb16d483 --- /dev/null +++ b/addons/docs/src/blocks/LegacyStory.tsx @@ -0,0 +1,102 @@ +import React, { FunctionComponent, ReactNode, ElementType, ComponentProps } from 'react'; +import { MDXProvider } from '@mdx-js/react'; +import { resetComponents, Story as PureStory } from '@storybook/components'; +import { toId, storyNameFromExport } from '@storybook/csf'; +import { Args, BaseAnnotations } from '@storybook/addons'; +import { CURRENT_SELECTION } from './types'; + +import { DocsContext, DocsContextProps } from './DocsContext'; + +export const storyBlockIdFromId = (storyId: string) => `story--${storyId}`; + +type PureStoryProps = ComponentProps; + +type CommonProps = BaseAnnotations & { + height?: string; + inline?: boolean; +}; + +type StoryDefProps = { + name: string; + children: ReactNode; +}; + +type StoryRefProps = { + id?: string; +}; + +type StoryImportProps = { + name: string; + story: ElementType; +}; + +export type StoryProps = (StoryDefProps | StoryRefProps | StoryImportProps) & CommonProps; + +export const lookupStoryId = ( + storyName: string, + { mdxStoryNameToKey, mdxComponentMeta }: DocsContextProps +) => + toId( + mdxComponentMeta.id || mdxComponentMeta.title, + storyNameFromExport(mdxStoryNameToKey[storyName]) + ); + +export const getStoryProps = (props: StoryProps, context: DocsContextProps): PureStoryProps => { + const { id } = props as StoryRefProps; + const { name } = props as StoryDefProps; + const inputId = id === CURRENT_SELECTION ? context.id : id; + const previewId = inputId || lookupStoryId(name, context); + const data = context.storyStore.fromId(previewId) || {}; + + const { height, inline } = props; + const { storyFn = undefined, name: storyName = undefined, parameters = {} } = data; + const { docs = {} } = parameters; + + if (docs.disable) { + return null; + } + + // prefer block props, then story parameters defined by the framework-specific settings and optionally overridden by users + const { inlineStories = false, iframeHeight = 100, prepareForInline } = docs; + const storyIsInline = typeof inline === 'boolean' ? inline : inlineStories; + if (storyIsInline && !prepareForInline) { + throw new Error( + `Story '${storyName}' is set to render inline, but no 'prepareForInline' function is implemented in your docs configuration!` + ); + } + + return { + parameters, + inline: storyIsInline, + id: previewId, + storyFn: prepareForInline && storyFn ? () => prepareForInline(storyFn, data) : storyFn, + height: height || (storyIsInline ? undefined : iframeHeight), + title: storyName, + }; +}; + +const Story: FunctionComponent = (props) => ( + + {(context) => { + const storyProps = getStoryProps(props, context); + + if (!storyProps) { + return null; + } + return ( +
+ + + +
+ ); + }} +
+); + +Story.defaultProps = { + children: null, + name: null, +}; + +export { Story }; diff --git a/addons/docs/src/blocks/ModernStory.tsx b/addons/docs/src/blocks/ModernStory.tsx new file mode 100644 index 000000000000..319519973b55 --- /dev/null +++ b/addons/docs/src/blocks/ModernStory.tsx @@ -0,0 +1,83 @@ +import React, { FunctionComponent, ReactNode, ElementType, useEffect } from 'react'; +import { MDXProvider } from '@mdx-js/react'; +import { resetComponents } from '@storybook/components'; +import { DOCS_TARGETTED_RENDER, DOCS_TARGETTED_DESTROY } from '@storybook/core-events'; +import { toId, storyNameFromExport } from '@storybook/csf'; +import { Args, BaseAnnotations, addons } from '@storybook/addons'; +import { CURRENT_SELECTION } from './types'; + +import { DocsContext, DocsContextProps } from './DocsContext'; + +export const storyBlockIdFromId = (storyId: string) => `story--${storyId}`; + +type CommonProps = BaseAnnotations & { + height?: string; + inline?: boolean; +}; + +type StoryDefProps = { + name: string; + children: ReactNode; +}; + +type StoryRefProps = { + id?: string; +}; + +type StoryImportProps = { + name: string; + story: ElementType; +}; + +export type StoryProps = (StoryDefProps | StoryRefProps | StoryImportProps) & CommonProps; + +export const lookupStoryId = ( + storyName: string, + { mdxStoryNameToKey, mdxComponentMeta }: DocsContextProps +) => + toId( + mdxComponentMeta.id || mdxComponentMeta.title, + storyNameFromExport(mdxStoryNameToKey[storyName]) + ); + +const Placeholder: FunctionComponent = ({ id, name }) => { + const channel = addons.getChannel(); + const identifier = storyBlockIdFromId(id); + useEffect(() => { + channel.emit(DOCS_TARGETTED_RENDER, { identifier, id, name }); + return () => { + channel.emit(DOCS_TARGETTED_DESTROY, { identifier, id, name }); + // TODO this does nothing, should it do something though? + }; + }); + + return ( +
+ loading story... +
+ ); +}; + +const Story: FunctionComponent = (props) => ( + + {(context) => { + const { id } = props as StoryRefProps; + const { name } = props as StoryDefProps; + const inputId = id === CURRENT_SELECTION ? context.id : id; + const previewId = inputId || lookupStoryId(name, context); + + return ( + + + + ); + }} + +); + +Story.defaultProps = { + children: null, + name: null, +}; + +export { Story }; diff --git a/addons/docs/src/blocks/Story.tsx b/addons/docs/src/blocks/Story.tsx index 319519973b55..0fc4c9120474 100644 --- a/addons/docs/src/blocks/Story.tsx +++ b/addons/docs/src/blocks/Story.tsx @@ -1,83 +1,8 @@ -import React, { FunctionComponent, ReactNode, ElementType, useEffect } from 'react'; -import { MDXProvider } from '@mdx-js/react'; -import { resetComponents } from '@storybook/components'; -import { DOCS_TARGETTED_RENDER, DOCS_TARGETTED_DESTROY } from '@storybook/core-events'; -import { toId, storyNameFromExport } from '@storybook/csf'; -import { Args, BaseAnnotations, addons } from '@storybook/addons'; -import { CURRENT_SELECTION } from './types'; +import global from 'global'; +import { Story as LegacyStory } from './LegacyStory'; +import { Story as ModernStory } from './ModernStory'; -import { DocsContext, DocsContextProps } from './DocsContext'; +export const Story = global?.MODERN_INLINE_RENDER ? ModernStory : LegacyStory; -export const storyBlockIdFromId = (storyId: string) => `story--${storyId}`; - -type CommonProps = BaseAnnotations & { - height?: string; - inline?: boolean; -}; - -type StoryDefProps = { - name: string; - children: ReactNode; -}; - -type StoryRefProps = { - id?: string; -}; - -type StoryImportProps = { - name: string; - story: ElementType; -}; - -export type StoryProps = (StoryDefProps | StoryRefProps | StoryImportProps) & CommonProps; - -export const lookupStoryId = ( - storyName: string, - { mdxStoryNameToKey, mdxComponentMeta }: DocsContextProps -) => - toId( - mdxComponentMeta.id || mdxComponentMeta.title, - storyNameFromExport(mdxStoryNameToKey[storyName]) - ); - -const Placeholder: FunctionComponent = ({ id, name }) => { - const channel = addons.getChannel(); - const identifier = storyBlockIdFromId(id); - useEffect(() => { - channel.emit(DOCS_TARGETTED_RENDER, { identifier, id, name }); - return () => { - channel.emit(DOCS_TARGETTED_DESTROY, { identifier, id, name }); - // TODO this does nothing, should it do something though? - }; - }); - - return ( -
- loading story... -
- ); -}; - -const Story: FunctionComponent = (props) => ( - - {(context) => { - const { id } = props as StoryRefProps; - const { name } = props as StoryDefProps; - const inputId = id === CURRENT_SELECTION ? context.id : id; - const previewId = inputId || lookupStoryId(name, context); - - return ( - - - - ); - }} - -); - -Story.defaultProps = { - children: null, - name: null, -}; - -export { Story }; +// FIXME: refactor +export { storyBlockIdFromId, lookupStoryId } from './ModernStory'; diff --git a/examples/react-ts/main.ts b/examples/react-ts/main.ts index 677f7382cd01..527c0170c01e 100644 --- a/examples/react-ts/main.ts +++ b/examples/react-ts/main.ts @@ -31,6 +31,7 @@ const config: StorybookConfig = { features: { postcss: false, previewCsfV3: true, + modernInlineRender: true, }, }; diff --git a/lib/builder-webpack4/src/preview/iframe-webpack.config.ts b/lib/builder-webpack4/src/preview/iframe-webpack.config.ts index 3bec5bc44b3e..0aa99040b093 100644 --- a/lib/builder-webpack4/src/preview/iframe-webpack.config.ts +++ b/lib/builder-webpack4/src/preview/iframe-webpack.config.ts @@ -65,6 +65,7 @@ export default async ({ typescriptOptions, modern, previewCsfV3, + modernInlineRender, }: Options & Record): Promise => { const logLevel = await presets.apply('logLevel', undefined); const frameworkOptions = await presets.apply(`${framework}Options`, {}); @@ -155,6 +156,7 @@ export default async ({ LOGLEVEL: logLevel, FRAMEWORK_OPTIONS: frameworkOptions, PREVIEW_CSF_V3: previewCsfV3, + MODERN_INLINE_RENDER: modernInlineRender, }, headHtmlSnippet, bodyHtmlSnippet, diff --git a/lib/builder-webpack5/src/preview/iframe-webpack.config.ts b/lib/builder-webpack5/src/preview/iframe-webpack.config.ts index 15e22f7e2725..17eacd1e6cf1 100644 --- a/lib/builder-webpack5/src/preview/iframe-webpack.config.ts +++ b/lib/builder-webpack5/src/preview/iframe-webpack.config.ts @@ -59,6 +59,8 @@ export default async ({ presets, typescriptOptions, modern, + previewCsfV3, + modernInlineRender, }: Options & Record): Promise => { const envs = await presets.apply>('env'); const logLevel = await presets.apply('logLevel', undefined); @@ -153,6 +155,8 @@ export default async ({ globals: { LOGLEVEL: logLevel, FRAMEWORK_OPTIONS: frameworkOptions, + PREVIEW_CSF_V3: previewCsfV3, + MODERN_INLINE_RENDER: modernInlineRender, }, headHtmlSnippet, bodyHtmlSnippet, diff --git a/lib/core-common/src/types.ts b/lib/core-common/src/types.ts index b53ce4ea8586..59ab5dcb2b07 100644 --- a/lib/core-common/src/types.ts +++ b/lib/core-common/src/types.ts @@ -156,6 +156,7 @@ export interface BuilderOptions { configDir: string; docsMode: boolean; previewCsfV3?: boolean; + modernInlineRender?: boolean; versionCheck?: VersionCheck; releaseNotesData?: ReleaseNotesData; disableWebpackDefaults?: boolean; @@ -251,6 +252,11 @@ export interface StorybookConfig { * Activate preview of CSF v3.0 */ previewCsfV3?: boolean; + + /** + * Activate modern inline rendering + */ + modernInlineRender?: boolean; }; /** * Tells Storybook where to find stories. diff --git a/lib/core-server/src/build-dev.ts b/lib/core-server/src/build-dev.ts index e205e8ad2ea3..a393a07cb071 100644 --- a/lib/core-server/src/build-dev.ts +++ b/lib/core-server/src/build-dev.ts @@ -79,6 +79,7 @@ export async function buildDevStandalone(options: CLIOptions & LoadOptions & Bui ...options, presets, previewCsfV3: features?.previewCsfV3, + modernInlineRender: features?.modernInlineRender, }; const { address, networkAddress, managerResult, previewResult } = await storybookDevServer( diff --git a/lib/core-server/src/build-static.ts b/lib/core-server/src/build-static.ts index 4f38bdf0d577..c0da50f89edc 100644 --- a/lib/core-server/src/build-static.ts +++ b/lib/core-server/src/build-static.ts @@ -80,6 +80,7 @@ export async function buildStaticStandalone(options: CLIOptions & LoadOptions & ...options, presets, previewCsfV3: features?.previewCsfV3, + modernInlineRender: features?.modernInlineRender, }; const core = await presets.apply<{ builder?: string }>('core');