How to dynamically mutate "args" in Storybook v6 from the component's action?

Viewed 9208

Let's see we have the simple component ToggleButton:

const ButtonComponent = Vue.component('ButtonComponent', {
  props: {
    value: Boolean
  },

  methods: {
    handleClick() {
      this.$emit('toggle');
    }
  },

  template: `
    <button 
      :class="value ? 'on' : 'off'"
      @click="handleClick"
    >
      Toggle
    </button>`
});

And the story for that component:

import ToggleButton from './ToggleButton.vue';

export default {
  title: 'ToggleButton',
  component: ToggleButton,

  argTypes: {
    onToggle: {
      action: 'toggle' // <-- instead of logging "toggle" I'd like to mutate `args.value` here
    }
  }
};

export const Default = (_args, { argTypes }) => ({
  components: { ToggleButton },
  props: Object.keys(argTypes),

  template: `
    <ToggleButton
      :value="value"
      :toggle="onToggle"
    />
  `
});

Default.args = {
  value: false
}

What I want to achieve is to handle toggle action inside the story and change value that I've used in Default.args object to change the button style by changing the class name from .off to .on.

1 Answers

I had the same exact issue, and kept looking for days, till I stumbled upon this github post: https://github.com/storybookjs/storybook/issues/12006

Currently in my React (am sure vue approach will be similar), I do following:

import React from 'react';
import CheckboxGroupElement from '../CheckboxGroup';
import { STORYBOOK_CATEGORIES } from 'elements/storybook.categories';
import { useArgs } from '@storybook/client-api';

export default {
  component: CheckboxGroupElement,
  title: 'Components/CheckboxGroup',
  argTypes: {
    onChange: {
      control: 'func',
      table: {
        category: STORYBOOK_CATEGORIES.EVENTS,
      },
    },
  },
  parameters: { actions: { argTypesRegex: '^on.*' } },
};

const Template = (args) => {
  const [_, updateArgs] = useArgs();

  const handle = (e, f) => {
// inside this function I am updating arguments, but you can call it anywhere according to your demand, the key solution here is using `useArgs()`
// As you see I am updating list of options with new state here
    console.log(e, f);
    updateArgs({ ...args, options: e });
  };
  return <CheckboxGroupElement {...args} onChange={handle} />;
};

export const CheckboxGroup = Template.bind({});
CheckboxGroup.storyName = 'CheckboxGroup';
CheckboxGroup.args = {
//Here you define default args for your story (initial ones)
  controller: { label: 'Group controller' },
  options: [
    { label: 'option 1', checked: true },
    { label: 'option 2', checked: false },
    { label: 'option 3', checked: false },
  ],
  mode: 'nested',
};
Related