diff --git a/code/renderers/svelte/src/components/PreviewRender.svelte b/code/renderers/svelte/src/components/PreviewRender.svelte index b2900a0ddb24..3a3ef4ad5801 100644 --- a/code/renderers/svelte/src/components/PreviewRender.svelte +++ b/code/renderers/svelte/src/components/PreviewRender.svelte @@ -15,6 +15,8 @@ props = {}, /** @type {{[string]: () => {}}} Attach svelte event handlers */ on, + /** @type {any} whether this level of the decorator chain is the last, ie. the actual story */ + argTypes, } = storyFn(); let firstTime = true; @@ -29,20 +31,16 @@ Component, props, on, + argTypes, }; } return storyFn(); } // reactive, re-render on storyFn change - $: ({ Component, props = {}, on } = getStoryFnValue(storyFn)); - - const eventsFromArgTypes = Object.fromEntries( - Object.entries(storyContext.argTypes) - .filter(([k, v]) => v.action && props[k] != null) - .map(([k, v]) => [v.action, props[k]]) - ); + $: ({ Component, props = {}, on, argTypes } = getStoryFnValue(storyFn)); + // set the argTypes context, read by the last SlotDecorator that renders the original story if (!Component) { showError({ title: `Expecting a Svelte component from the story: "${name}" of "${title}".`, @@ -55,4 +53,4 @@ } - + diff --git a/code/renderers/svelte/src/components/SlotDecorator.svelte b/code/renderers/svelte/src/components/SlotDecorator.svelte index 8391eb0af6e3..27b80aae3412 100644 --- a/code/renderers/svelte/src/components/SlotDecorator.svelte +++ b/code/renderers/svelte/src/components/SlotDecorator.svelte @@ -6,20 +6,36 @@ export let Component; export let props = {}; export let on = undefined; + export let argTypes = undefined; let instance; let decoratorInstance; const svelteVersion = VERSION[0]; + + /* + Svelte Docgen will create argTypes for events with the name 'event_eventName' + The Actions addon will convert these to args because they are type: 'action' + We need to filter these args out so they are not passed to the component + */ + let propsWithoutDocgenEvents; + $: propsWithoutDocgenEvents = Object.fromEntries( + Object.entries(props).filter(([key]) => !key.startsWith('event_')) + ); - if (on && svelteVersion < 5) { + if (argTypes && svelteVersion < 5) { + const eventsFromArgTypes = Object.fromEntries( + Object.entries(argTypes) + .filter(([key, value]) => value.action && props[key] != null) + .map(([key, value]) => [value.action, props[key]]) + ); // Attach Svelte event listeners in Svelte v4 // In Svelte v5 this is not possible anymore as instances are no longer classes with $on() properties, so it will be a no-op onMount(() => { - Object.entries(on).forEach(([eventName, eventCallback]) => { - // instance can be undefined if a decorator doesn't have + Object.entries({ ...eventsFromArgTypes, ...on }).forEach(([eventName, eventCallback]) => { + // instance can be undefined if a decorator doesn't have a const inst = instance ?? decoratorInstance; - inst?.$on?.(eventName, eventCallback) + inst?.$on?.(eventName, eventCallback); }); }); } @@ -27,8 +43,8 @@ {#if decorator} - + {:else} - + {/if} diff --git a/code/renderers/svelte/src/decorators.ts b/code/renderers/svelte/src/decorators.ts index 15ba2acf4831..200c6b41f006 100644 --- a/code/renderers/svelte/src/decorators.ts +++ b/code/renderers/svelte/src/decorators.ts @@ -74,7 +74,8 @@ function prepareStory( }; } - return preparedStory; + // no innerStory means this is the last story in the decorator chain, so it should create events from argTypes + return { ...preparedStory, argTypes: context.argTypes }; } export function decorateStory(storyFn: any, decorators: any[]) {