From 12dcacc5d345118805371d359999d12043a7be82 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 2 Nov 2022 02:20:16 +0100 Subject: [PATCH] dont remount Svelte components on control change, make template stories reactive --- code/renderers/svelte/src/render.ts | 68 +++++++++++++++---- .../svelte/template/cli/Button.svelte | 4 +- .../svelte/template/components/Button.svelte | 4 +- .../svelte/templates/PreviewRender.svelte | 25 +++++-- .../svelte/templates/SlotDecorator.svelte | 7 +- 5 files changed, 80 insertions(+), 28 deletions(-) diff --git a/code/renderers/svelte/src/render.ts b/code/renderers/svelte/src/render.ts index 4e2c6072dfa..b7d6dce1a00 100644 --- a/code/renderers/svelte/src/render.ts +++ b/code/renderers/svelte/src/render.ts @@ -1,3 +1,4 @@ +import global from 'global'; import type { Store_RenderContext, ArgsStoryFn } from '@storybook/types'; import type { SvelteComponentTyped } from 'svelte'; // eslint-disable-next-line import/no-extraneous-dependencies @@ -5,38 +6,77 @@ import PreviewRender from '@storybook/svelte/templates/PreviewRender.svelte'; import type { SvelteFramework } from './types'; -const componentsByDomElementId = new Map(); +const componentsByDomElementKey = new Map(); -function cleanupExistingComponent(domElementKey: string) { - if (!componentsByDomElementId.has(domElementKey)) { +const STORYBOOK_ROOT_ID = 'storybook-root'; + +function teardown(domElementKey: string) { + if (!componentsByDomElementKey.has(domElementKey)) { return; } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- we know it exists because we just checked - componentsByDomElementId.get(domElementKey)!.$destroy(); - componentsByDomElementId.delete(domElementKey); + componentsByDomElementKey.get(domElementKey)!.$destroy(); + + // in docs mode, the element we render to is a child of the element with the story id + // TODO: this feels brittle. If we ever change the structure in the component, this will break + const rootElement = + domElementKey === STORYBOOK_ROOT_ID + ? global.document.getElementById(domElementKey) + : global.document.getElementById(domElementKey).children[0]; + rootElement.innerHTML = ''; + componentsByDomElementKey.delete(domElementKey); } export function renderToDOM( - { storyFn, kind, name, showMain, showError, storyContext }: Store_RenderContext, + { + storyFn, + kind, + name, + id, + showMain, + showError, + storyContext, + forceRemount, + }: Store_RenderContext, domElement: Element ) { // in docs mode we're rendering multiple stories to the DOM, so we need to key by the story id - const domElementKey = storyContext.viewMode === 'docs' ? storyContext.id : 'storybook-root'; - cleanupExistingComponent(domElementKey); + const domElementKey = storyContext.viewMode === 'docs' ? id : STORYBOOK_ROOT_ID; - const renderedComponent = new PreviewRender({ - target: domElement, - props: { + const existingComponent = componentsByDomElementKey.get(domElementKey); + + if (forceRemount) { + teardown(domElementKey); + } + + if (!existingComponent || forceRemount) { + const createdComponent = new PreviewRender({ + target: domElement, + props: { + storyFn, + storyContext, + name, + kind, + showError, + }, + }) as SvelteComponentTyped; + componentsByDomElementKey.set(domElementKey, createdComponent); + } else { + existingComponent.$set({ storyFn, storyContext, name, kind, showError, - }, - }); - componentsByDomElementId.set(domElementKey, renderedComponent); + }); + } showMain(); + + // teardown the component when the story changes + return () => { + teardown(domElementKey); + }; } export const render: ArgsStoryFn = (args, context) => { diff --git a/code/renderers/svelte/template/cli/Button.svelte b/code/renderers/svelte/template/cli/Button.svelte index 09b9bdbe5bc..5a19ed6d681 100644 --- a/code/renderers/svelte/template/cli/Button.svelte +++ b/code/renderers/svelte/template/cli/Button.svelte @@ -19,9 +19,9 @@ */ export let label = ''; - let mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; + $: mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; - let style = backgroundColor ? `background-color: ${backgroundColor}` : ''; + $: style = backgroundColor ? `background-color: ${backgroundColor}` : ''; const dispatch = createEventDispatcher(); diff --git a/code/renderers/svelte/template/components/Button.svelte b/code/renderers/svelte/template/components/Button.svelte index 9f4f6935708..4b80e84b813 100644 --- a/code/renderers/svelte/template/components/Button.svelte +++ b/code/renderers/svelte/template/components/Button.svelte @@ -21,9 +21,9 @@ */ export let label = ''; - let mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; + $: mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; - let style = backgroundColor ? `background-color: ${backgroundColor}` : ''; + $: style = backgroundColor ? `background-color: ${backgroundColor}` : ''; const dispatch = createEventDispatcher(); diff --git a/code/renderers/svelte/templates/PreviewRender.svelte b/code/renderers/svelte/templates/PreviewRender.svelte index bfdab9a223d..ef9168d3a54 100644 --- a/code/renderers/svelte/templates/PreviewRender.svelte +++ b/code/renderers/svelte/templates/PreviewRender.svelte @@ -8,7 +8,14 @@ export let showError; export let storyContext; - const { + let Component; + let props = {}; + let on; + let Wrapper; + let WrapperData = {}; + + // reactive, re-render on storyFn change + $: ({ /** @type {SvelteComponent} */ Component, /** @type {any} */ @@ -17,13 +24,15 @@ on, Wrapper, WrapperData = {}, - } = storyFn(); + } = storyFn()); - const eventsFromArgTypes = Object.fromEntries(Object.entries(storyContext.argTypes) + const eventsFromArgTypes = Object.fromEntries( + Object.entries(storyContext.argTypes) .filter(([k, v]) => v.action && props[k] != null) - .map(([k, v]) => [v.action, props[k]])); + .map(([k, v]) => [v.action, props[k]]) + ); - const events = {...eventsFromArgTypes, ...on}; + const events = { ...eventsFromArgTypes, ...on }; if (!Component) { showError({ @@ -36,9 +45,11 @@ }); } + \ No newline at end of file + {props} + on={events} +/> diff --git a/code/renderers/svelte/templates/SlotDecorator.svelte b/code/renderers/svelte/templates/SlotDecorator.svelte index bd51e06f427..6b0d0633700 100644 --- a/code/renderers/svelte/templates/SlotDecorator.svelte +++ b/code/renderers/svelte/templates/SlotDecorator.svelte @@ -21,10 +21,11 @@ }); } + {#if decorator} - + {:else} - -{/if} \ No newline at end of file + +{/if}