Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storybook Vue3 - Work with v-model in stories

I have a question regarding Storybook and Vue components with v-models. When writing a story for let's say an input component with a v-model i want a control reflecting the value of this v-model. Setting the modelValue from the control is no problem, but when using the component itself the control value stays the same. I am searching the web for a while now but i can't seem to find a solution for this.

A small example:

// InputComponent.vue
<template>
  <input
      type="text"
      :value="modelValue"
      @input="updateValue"
      :class="`form-control${readonly ? '-plaintext' : ''}`"
      :readonly="readonly"
  />
</template>

<script lang="ts">
    export default {
        name: "GcInputText"
    }
</script>

<script lang="ts" setup>
    defineProps({
        modelValue: {
            type: String,
            default: null
        },
        readonly: {
            type: Boolean,
            default: false
        }
    });

    const emit = defineEmits(['update:modelValue']);

    const updateValue = (event: Event) => {
        const target = event.target as HTMLInputElement;
        emit('update:modelValue', target.value);
    }
</script>

In Storybook: enter image description here

enter image description here

Does anyone have a solution to make this working?

Thanks in advance!

like image 537
Kim Boender Avatar asked Sep 21 '25 12:09

Kim Boender


2 Answers

In my case, I have a custom select input that uses a modelValue prop.

I tried this and worked for me:

at my-component.stories.js:

import { ref } from 'vue'
import MyComponent from './MyComponent.vue'

export default {
  title: 'Core/MyComponent',
  component: MyComponent,
  argTypes: { }
}

const Template = (args) => ({
  components: { MyComponent },
  setup() {
    let model = ref('Javascript')
    const updateModel = (event) => model.value = event

    return { args, model, updateModel }
  },
  template: '<my-component v-bind="args" :modelValue="model" @update:modelValue="updateModel" />'
})

export const Default = Template.bind({})
Default.args = {
  options: [
    'Javascript',
    'PHP',
    'Java'
  ]
}
like image 134
Lana Avatar answered Sep 23 '25 03:09

Lana


You can also use v-model directly:

import type { Meta, StoryObj } from '@storybook/vue3';
import { ref, watch } from 'vue'
import MyComponent from './MyComponent.vue'

export default {
  title: 'Core/MyComponent',
  component: MyComponent,
} satisfies Meta<typeof MyComponent>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Example: Story = {
  render: (args) => ({
    components: { MyComponent },
    setup() {
      const model = ref(args.modelValue);

      // Optional: Keeps v-model in sync with storybook args
      watch(
        () => args.modelValue,
        (val) => {
          model.value = val;
        },
      );

      return { args, model };
    },
    template: '<MyComponent v-bind="args" v-model="model" />',
  }),
  args: {
    modelValue: 'Value',
  },
};

I am using Storybook version 8.1.1 in this solution. You could probably also keep the v-model value in sync by using writable computed property, but watcher works too.

like image 33
Crossoni Avatar answered Sep 23 '25 02:09

Crossoni