#Two-way data binding in Vue 3?

47 messages · Page 1 of 1 (latest)

fallow yacht
#

Hi, what's the correct way to handle v-model in Storybook stories? Example:

import DataPaginator from '../DataPaginator.vue';
import type { Meta, StoryObj } from '@storybook/vue3';
import { action } from '@storybook/addon-actions';

const meta: Meta<typeof DataPaginator> = {
  title: 'Shared/DataPaginator',
  component: DataPaginator,
  tags: ['autodocs'],
};

export const Basic: StoryObj<typeof DataPaginator> = {
  args: {
    itemsLength: 83,
    itemsPerPage: 10,
    page: 1,
    'onUpdate:items-per-page': action('onUpdate:items-per-page'),
    'onUpdate:page': action('onUpdate:page'),
  },
};

export default meta;

this works fine, like when I change the page or items per page, I see the event in Storybook. but it doesn't actually update the page number. how can I make it so onUpdate:page will actually dynamically alter the page prop?

#

it seems like this works, though without args, the controls section is disabled in Storybook

export const Basic: StoryObj<typeof DataPaginator> = {
  render: () => ({
    components: { DataPaginator },
    setup() {
      const itemsLength = 83;
      const itemsPerPage = 10;
      const page = ref(1);

      return { itemsLength, itemsPerPage, page };
    },
    template: `
      <DataPaginator
        v-model:page="page"
        align="left"
        :items-length="itemsLength"
        :items-per-page="itemsPerPage"
      />
    `,
  }),
};
fallow yacht
#

or maybe it's good practice here to not use 2-way data binding, and just let the user control via Storybook controls.. ?

fallow yacht
#

@chilly igloo I suppose this isn't specifically a Vue question.. more of a Storybook best practice thing. if I have something like a switch that you flip on/off, is it better to write the story so that it actually updates that reactive value? or hitting the switch does nothing, and the user controls it via Storybook controls?

chilly igloo
#

I think it’s a matter of preference. I normally don’t use controls, and instead add some kind of helper component to put the switch inside of, to show the behavior when clicking it. But that’s just my own preference.

fallow yacht
#

I see. I'll have to find more examples of stories and get a feel for it

chilly igloo
#

There may also be a way to wire it straight to the controls, but I’m not sure.

fallow yacht
#

yeah, if I use render, then I can make the actual component react to user interaction

#

but it's either/or. if I use render, the controls don't work

#

or I can wipe out render and just use args, then controls work

chilly igloo
#

You could probably still react to args with your custom render, right?

#

Render does receive the args

fallow yacht
#

hmm, I'll give it another shot. I think I had some trouble with it

#

thanks for your help! 🙂

chilly igloo
#

@austere cape is our Vue maintainer, he may have better suggestions

austere cape
#

Hello @fallow yacht yes you should

#

Let me check this case I will get back you, I was already working on some fix, regarding this case

fallow yacht
austere cape
#

you don't need to use refs in your story as the args are already bind to it

#

Two-way data binding should be in your component and not on the Story,
And i don't really recommend using 2-way data binding unless you really need it.

fallow yacht
#

the only way to make it switch on/off is via the Storybook controls

austere cape
#

can you share your code with me and screenshot with issue

fallow yacht
austere cape
#

welcome anytime @fallow yacht

fallow yacht
#

story:

import { ref } from 'vue';
import M3Switch from '../M3Switch.vue';
import type { Meta, StoryObj } from '@storybook/vue3';
import { action } from '@storybook/addon-actions';

const meta: Meta<typeof M3Switch> = {
  title: 'Shared/M3Switch',
  component: M3Switch,
  tags: ['autodocs'],
};

export const Basic: StoryObj<typeof M3Switch> = {
  // render: () => ({
  //   components: { M3Switch },
  //   setup() {
  //     const switched = ref(false);

  //     return { switched };
  //   },
  //   template: '<M3Switch v-model="switched" />',
  // }),
  args: {
    label: 'Do something',
    color: 'secondary',
    modelValue: false,
    'onUpdate:modelValue': action('onUpdate:modelValue'),
  },
};

export default meta;
#

also, interesting bug: onUpdate:modelValue shows up in controls twice. the very top one doesn't really apply

#

well, not quite twice.. the real event shows up as update:modelValue. but I can't pass that into args since that's not the correct format.. args needs onUpdate:modelValue

#

here's another approach:

export const Basic: StoryObj<typeof M3Switch> = {
  render: (args) => ({
    components: { M3Switch },
    setup() {
      const switched = ref(false);

      return { args, switched };
    },
    template: '<M3Switch v-bind="args" v-model="switched" />',
  }),
  args: {
    label: 'Do something',
    color: 'secondary',
  },
};

now I can actually click the switch myself.

#

and in the controls, modelValue has a button, "Set boolean" where the user can override it

#

just not sure what the right approach here is

#

so I suppose the crux of the issue is: the args property is not mutable. it's cool that we can use this to generate controls the user can modify to alter the state of the component. but if they interact directly w/ the component, it can't write to args (that I know of)

#

it would be super super cool if the component sync'd with the Storybook controls

austere cape
#

yes it should otherwise there is no point

#

i'll check it out in 30 min and get back to you @fallow yacht

fallow yacht
#

much appreciated! 🙏

#

I'm on 7.0.0-beta.53 BTW

fallow yacht
#

@austere cape just curious if you discovered anything. I'm onboarding a developer and we're about to write a massive ton of Storybook stories, so we wanna do it right 🙂

austere cape
#

Hi @fallow yacht can you share you me the switch component to do some tests, if you can create a shared repo so i can help you and your developer to get started, I'm working on a PR to fix this it will be ready by Monday !

fallow yacht
fallow yacht
#

as you can see, you can use Storybook controls to update the component.. but not the other way around. interacting w/ the component does not update the controls at the bottom

#

so the UglySwitch, for instance, doesn't do anything when clicked

austere cape
#

Good morning @fallow yacht thanks for the reproduction repo, 2 ways data binding will never be 100% adopted as it is dangerous but we can have some explicit settings maybe if necessary i will be looking into, but the controls should be synched with the story state, i think here maybe fire some update event will fix the prb

fallow yacht
austere cape
#

Yes normally you should be testing your component by changing the state using args in Stories for the same component or the controls in interactive way.