storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
83.77k stars 9.18k forks source link

Trigger Vue event with $event happen `method "toJSON" is not defined` by Addon Action #14933

Open VisionYi opened 3 years ago

VisionYi commented 3 years ago

Describe the bug When adding the $event props for the event method of Vue component and trigger the event on Storybook button, the Addon Action and Vue will happen the error message below: [Vue warn]: Property or method "toJSON" is not defined on the instance but referenced during render

image bug

To Reproduce

  1. I create an application with the command: npx vue create vue-cli-storybook with vue-router, vuex and eslint. (@vue/cli 4.5.13)
  2. I install the Storybook with the command: npx sb init
  3. Then, I add the $event props on onClick method on the example file ./src/stories/Button.vue like this:
    methods: {
    onClick($event) {
    this.$emit('onClick', $event);
    },
    },
  4. run storybook npm run storybook and open Addon Actions console to trigger the button event.

The example repository: https://github.com/VisionYi/vue-cli-storybook

System

Additional context I think it is related to this issue [Addon Action in v5]: Issue when serializing a Vue instance.

In my real project with Storybook v6.1.x that didn't happen like this error, but I upgrade the version to v6.2.9 just have this problem...

In addition, this problem causes severe delay to trigger the event.

metal-gogo commented 3 years ago

I had the same issue, but for me it is only present when I have v-on="$listeners" on my component. Like this:

<template>
  <button v-on="$listeners">
    <slot />
  </button>
</template>
VisionYi commented 3 years ago

Hi, @shilman Could the Storybook team fix this issue? or any plan to improve that?

shilman commented 3 years ago

@VisionYi does the workaround in #14933 work for you?

VisionYi commented 3 years ago

@shilman I only can replace $event with a clear value as a prop, it can avoid the error message that happened. Avoid Vue event object as the prop to pass to the parent component. But it is not a workaround.

By the way, https://github.com/storybookjs/storybook/issues/14933#issuecomment-862621433 is also not a workaround.

reutgrubervcita commented 3 years ago

Hi,

I am also experiencing the above issue, the differences being that:

  1. my package version are as follows: "@storybook/addon-actions": "^6.3.0", "@storybook/addon-essentials": "^6.3.0", "@storybook/vue": "^6.3.0"

  2. I am using the actions through the args property on the template: Button.args = { onClick: action('clicked'), }

The result I see is:

  1. an immediate action is printed in the actions panel, containing the event that was emitted from the tested component and accompanied by the console message regarding the "toJSON" method
  2. then a pretty long pause where the browser is frozen
  3. and finally an additional action is displayed in the actions panel.

Please note that that last event contains eventPhase: 3, so I assume it is displayed as a result of the bubbling phase of the event.

Thanks!

LawrenceB5477 commented 3 years ago

I'm also experiencing this issue. I need my vue component to emit the native event because I'm going to be using it in a library. Is there a solution for this?

reutgrubervcita commented 3 years ago

I think I figured it out to some extent, though with some limitations.

make sure to have this in your preview.js file export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, This will cause storybook to automatically generate action objects for each action that the component dispatches, which matches the above matcher. so this forced me to change the name of my event from "click" to "onClick", because otherwise it did not get picked up.

now my template in the story looks something like this: template: '<Button :label="label" :size="size" :type="type" @onClick="onClick"/>', and I don't define the onClick function anywhere in my file, it is created for me by storybook.

and now when I click my button, I don't get an error and only one action shows up in the actions tab.

I don't like that it forces me to change the event name, but I guess I can live with it, or take the time to play around with the regex.

VisionYi commented 2 years ago

I found the issue source:

  1. @storybook/addon-actions use the Telejson library to stringify the props ($event) of Vue event and show them on the Actions console.
  2. Telejson stringify the props ($event) by JSON.stringify and do some conversion processes to be an object (?)
  3. JSON.stringify will call property named toJSON. (reference link)
  4. Vue will warn about an undeclared method being referenced during render.
    [Vue warn]: Property or method "toJSON" is not defined on the instance but referenced during render

Then, I try below testing: (change $event to this)

// Button.vue
methods: {
  onClick($event) {
    this.$emit('onClick', this);
  },
},

It also will get the same error warning. I think this issue is Telejson caused a puzzling error in Vue.

The workaround is to insert the toJSON method in the Vue instance or component instance, like:

// Button.stories.js
const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { MyButton },
  template: '<my-button @onClick="onClick" v-bind="$props" />',
  methods: { toJSON: () => {} },
});

or Vue top-level prototype, you can put it on .stroybook/preview.js file:

Vue.prototype.toJSON = () => {}

Actually, please put this solution on the Addon Actions Readme.md, because it is a special error case for Vue and depend on the Telejson issue or technical limitations.

VisionYi commented 2 years ago

@metal-gogo @reutgrubervcita @LawrenceB5477 You can try the workaround above on your project.

mikemklee commented 2 years ago

Any updates on this issue?

DiegoLopesLima commented 1 year ago

Any updates on this issue?