You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I would like an easy way to have story state that is bound to the component but can still be controlled via args.
When writing stories for React it's very simple to have story state where the initial value is based on the story args, e.g.
constTemplate=(args)=>{// Initial state is based on the argsconst[isOpen,setOpen]=useState(args.isOpen);return<Accordion{...args}isOpen={isOpen}onChange={setOpen}/>;};exportconstOpen={render: Template,args: {isOpen: true},};exportconstClosed={render: Template,args: {isOpen: false},};
This allows you to have stories that 1) share a template, 2) show how the component reacts to different props, 3) are still fully interactive!
As a bonus, making it so the story state reacts to changes in the "Controls" panel is a bit more work but still straightforward:
constTemplate=(args)=>{// Initial state is based on the argsconst[isOpen,setOpen]=useState(args.isOpen);// State updates when the args are changeduseEffect(()=>{setOpen(args.isOpen);},[args.isOpen]);return<Accordion{...args}isOpen={isOpen}onChange={setOpen}/>;};
Reproducing this in addon-svelte-csf has proven difficult because I haven't found an easy way to access the story args in the initial render of the story. I've come up with a workaround (see below) but it feels very hacky and I would love to know if there's a built-in way to do it instead.
EDIT: See my comment below the OP, I ended up coming up with a much nicer workaround.
Example workaround
Note: This workaround has a limitation in that changes to state that happen inside the story code (e.g. form submission changing the loading state) will not update the story args, but this is the same as in my React example above and I think this is acceptable if not expected.
<scriptcontext="module"lang="ts">importtype{Meta}from'@storybook/svelte';importFormfrom'./Form.svelte';exportconstmeta={title: 'Form',component: Form,argTypes: {loading: {control: 'boolean'},error: {control: 'text'},},args: {loading: false,error: '',},}satisfiesMeta<Form>;</script><scriptlang="ts">import{ beforeUpdate, onMount }from'svelte';import{Story,Template}from'@storybook/addon-svelte-csf';letloading=false;leterror='';asyncfunctionhandleSubmit(){loading=true;error='';setTimeout(()=>{loading=false;error='An error occurred, please try again.';},1000);}// WORKAROUND: Set the initial state from the initial story argsletfirstUpdate=true;beforeUpdate(()=>{constid=__STORYBOOK_PREVIEW__.currentRender.id;constargs=__STORYBOOK_PREVIEW__.currentRender.store.args.argsByStoryId[id];if(args&&firstUpdate){loading=args.loading;error=args.error;firstUpdate=false;}});// WORKAROUND: Listen for changes to the story args and update the stateonMount(()=>{functionhandleArgsUpdated({ args }){loading=args.loading;error=args.error;}__STORYBOOK_PREVIEW__.channel.on('storyArgsUpdated',handleArgsUpdated);return()=>__STORYBOOK_PREVIEW__.channel.off('storyArgsUpdated',handleArgsUpdated);});</script><Template><Formbind:loadingbind:erroron:submit={handleSubmit}/></Template><Storyname="Default"/><Storyname="Loading"args={{loading: true}}/>
<Storyname="Error"args={{error: "We couldn't find an account with that email address."}}/>
The text was updated successfully, but these errors were encountered:
Okay so I dug in to the source code a bit more and actually ended up coming up with a much nicer workaround than the one in the OP (4 lines of code instead of 18). However I am still curious to know if this is indeed a supported use case and/or if there's a nicer way to do it.
<scriptlang="ts">import{ getContext }from'svelte';import{Story,Template}from'@storybook/addon-svelte-csf';letloading=false;leterror='';// WORKAROUND: Update state to match the args on mount and when the args change// (Much more concise than the alternative in the OP)const{ argsStore }=getContext('storybook-registration-context-component')||{};
$: if(argsStore){({ loading, error }=$argsStore);}asyncfunctionhandleSubmit(){loading=true;error='';setTimeout(()=>{loading=false;error='An error occurred, please try again.';},1000);}</script><Template><Formbind:loadingbind:erroron:submit={handleSubmit}/></Template>
EDIT: Third time's the charm? After playing with the above solution a bit more I realised that changes to the bound variables from inside the component could reset the state to match the initial args. This is my latest iteration which avoids that issue...
Issue
I would like an easy way to have story state that is bound to the component but can still be controlled via args.
When writing stories for React it's very simple to have story state where the initial value is based on the story args, e.g.
This allows you to have stories that 1) share a template, 2) show how the component reacts to different props, 3) are still fully interactive!
As a bonus, making it so the story state reacts to changes in the "Controls" panel is a bit more work but still straightforward:
Reproducing this in
addon-svelte-csf
has proven difficult because I haven't found an easy way to access the story args in the initial render of the story. I've come up with a workaround (see below) but it feels very hacky and I would love to know if there's a built-in way to do it instead.EDIT: See my comment below the OP, I ended up coming up with a much nicer workaround.
Example workaround
Note: This workaround has a limitation in that changes to state that happen inside the story code (e.g. form submission changing the loading state) will not update the story args, but this is the same as in my React example above and I think this is acceptable if not expected.
Form.svelte
:Form.stories.svelte
:The text was updated successfully, but these errors were encountered: