Storybook: Vue components not reactively updating on knob change

Created on 1 Jan 2019  路  18Comments  路  Source: storybookjs/storybook

I've noticed that though Vue is support according to the docs, the knobs have zero effect on the state of the component. Only during initial load the default value of the knob is set.

Steps to reproduce:
Its actually just the normal setup, but its easiest with the Vue CLI:

  1. Use Vue CLI to create a new project: vue create stuff
  2. Add the storybook plugin: vue add storybook
  3. Add the addon: npm i -S @storybook/addon-knobs plugin
  4. Register it on the addons folder: import '@storybook/addon-knobs/register';
  5. Add a knob to one of the example components like this:
import { storiesOf } from '@storybook/vue';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';


import MyButton from '../components/MyButton.vue';

storiesOf('Button', module)
  .addDecorator(withKnobs)
  .add('with text', () => ({
    components: { MyButton },
    template: `<my-button @click="action">${text('Title', 'Hello!')}</my-button>`,
    methods: { action: action('clicked') },
  }));

Expected behavior
Changing the text knob should change the title text

System:

  • Tested on Linux Fedora (Node 8 & 10)

Additional context
My package.json for the deps and scripts:

{
  "name": "stuff",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "storybook:build": "vue-cli-service storybook:build -c config/storybook",
    "storybook:serve": "vue-cli-service storybook:serve -p 6006 -c config/storybook"
  },
  "dependencies": {
    "@storybook/addon-knobs": "^4.1.4",
    "vue": "^2.5.21"
  },
  "devDependencies": {
    "@storybook/addon-actions": "^4.1.0",
    "@storybook/addon-links": "^4.1.0",
    "@vue/cli-plugin-babel": "^3.2.0",
    "@vue/cli-plugin-eslint": "^3.2.0",
    "@vue/cli-service": "^3.2.0",
    "@vue/eslint-config-airbnb": "^4.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.8.0",
    "eslint-plugin-vue": "^5.0.0",
    "storybook-addon-smart-knobs": "^4.1.0",
    "vue-cli-plugin-storybook": "^0.5.0",
    "vue-template-compiler": "^2.5.21"
  }
}

knobs vue bug

Most helpful comment

This issue is also being discussed in https://github.com/storybooks/storybook/issues/4947.

Apparently, placing dynamic variables into string templates as outlined in the documentation no longer works.

A relatively recent change now asks that you specify the knobs as defaults of props.

Your stories should then look something like this:

import { storiesOf } from '@storybook/vue'
import { withKnobs, text, number } from '@storybook/addon-knobs'
import MyComponent from './MyComponent'

const storybook = storiesOf('MyComponent', module)
storybook.addDecorator(withKnobs({
  escapeHTML: false,
}))

storybook.add('default story', () => {
  return {
    components: { MyComponent },
    props: {
      myTextProp: {
        type: String,
        default: text('myTextProp', 'my text prop value')
      },
      myNumberPropWithRange: {
        type: Number,
        default: number('myNumberPropWithRange', 50, {
          range: true,
          min: 0,
          max: 100,
          step: 1
        })
      },
    },
    template: `
      <MyComponent
        :myTextProp="myTextProp"
        :myNumberPropWithRange="myNumberPropWithRange"
      />
    `
  }
})

I can verify that the above pattern works in 4.1.4, despite it not being documented. I spent yesterday adapting my story factory to abide by it and have everything working again.

All 18 comments

I have the same reactivity problem.
But my platform

System:
Tested on Linux Ubuntu 18.04 (Node 10)

And this in my package.json

{
  "name": "www",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "vue": "^2.5.21",
    "yarn": "^1.12.3"
  },
  "devDependencies": {
    "@babel/core": "^7.2.2",
    "@storybook/addon-actions": "^4.1.4",
    "@storybook/addon-knobs": "^4.1.4",
    "@storybook/addon-links": "^4.1.4",
    "@storybook/addon-notes": "^4.1.4",
    "@storybook/addon-options": "^4.1.4",
    "@storybook/addons": "^4.1.4",
    "@storybook/vue": "^4.1.4",
    "babel-loader": "^8.0.4",
    "babel-preset-vue": "^2.0.2",
    "vue-loader": "^15.4.2",
    "vue-template-compiler": "^2.5.21"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "storybook": "start-storybook -p 9001 -c .storybook"
  },
  "author": "ElDarco",
  "license": "MIT"
}

Same problem here 馃槩

This issue is also being discussed in https://github.com/storybooks/storybook/issues/4947.

Apparently, placing dynamic variables into string templates as outlined in the documentation no longer works.

A relatively recent change now asks that you specify the knobs as defaults of props.

Your stories should then look something like this:

import { storiesOf } from '@storybook/vue'
import { withKnobs, text, number } from '@storybook/addon-knobs'
import MyComponent from './MyComponent'

const storybook = storiesOf('MyComponent', module)
storybook.addDecorator(withKnobs({
  escapeHTML: false,
}))

storybook.add('default story', () => {
  return {
    components: { MyComponent },
    props: {
      myTextProp: {
        type: String,
        default: text('myTextProp', 'my text prop value')
      },
      myNumberPropWithRange: {
        type: Number,
        default: number('myNumberPropWithRange', 50, {
          range: true,
          min: 0,
          max: 100,
          step: 1
        })
      },
    },
    template: `
      <MyComponent
        :myTextProp="myTextProp"
        :myNumberPropWithRange="myNumberPropWithRange"
      />
    `
  }
})

I can verify that the above pattern works in 4.1.4, despite it not being documented. I spent yesterday adapting my story factory to abide by it and have everything working again.

This is because template strings are now immutable, as they are in Vue components.

Another upshot is that you're properties are now reactive instead of the template being thrown away and recreated each time.

Another upshot is that your properties are now reactive instead of the template being thrown away and recreated each time.

Reactivity for slots is broken, unfortunately.

Another upshot is that your properties are now reactive instead of the template being thrown away and recreated each time.

Reactivity for slots is broken, unfortunately.

In what way? I can do this

template: `
<my-component>
  <template slot="test">prop value is: {{propValue}}</template>
</my-component>`;
props: {
  propValue: { type: String, default: text('propValue', 'test') }
}

This, however, is not permissible

template: `
<my-component>
  <tempalte slot="test">${test('propValue', 'test')}</template>
</my-component>`

Ah! Thanks for the correction. I hadn't considered doing it that way. I'm readying a PR with updated documentation right now, so I'm glad you illustrated that for me.

Even with the knobs as props, the following story is not updated when a knob is changed. I tried with @latest (4.1.6) and @next (4.2.0-alpha.11). This is using a global decorator, but I tried moving it to the story as well without luck. Any ideas?

import { storiesOf } from '@storybook/vue';
import { boolean, text } from '@storybook/addon-knobs';
import { CheckBox } from '/path/to/MyCheckBoxComponent.vue';

storiesOf('Forms|CheckBoxes', module)
  .add('Default', () => ({
    components: { CheckBox },
    props: {
      isChecked: { type: Boolean, default: boolean('checked', true) },
      color: { type: String, default: text('color', '#e64a19') },
      className: { type: String, default: text('class', '') },
    },
    template: `<check-box :class="className" :checked="isChecked" :color="color"></check-box>`,
  }));

Even with the knobs as props, the following story is not updated when a knob is changed. I tried with @latest (4.1.6) and @next (4.2.0-alpha.11). This is using a global decorator, but I tried moving it to the story as well without luck. Any ideas?

import { storiesOf } from '@storybook/vue';
import { boolean, text } from '@storybook/addon-knobs';
import { CheckBox } from '/path/to/MyCheckBoxComponent.vue';

storiesOf('Forms|CheckBoxes', module)
  .add('Default', () => ({
    components: { CheckBox },
    props: {
      isChecked: { type: Boolean, default: boolean('checked', true) },
      color: { type: String, default: text('color', '#e64a19') },
      className: { type: String, default: text('class', '') },
    },
    template: `<check-box :class="className" :checked="isChecked" :color="color"></check-box>`,
  }));

Shouldn't that be

import { storiesOf } from '@storybook/vue';
import { withKnobs, boolean, text } from '@storybook/addon-knobs';
import { CheckBox } from '/path/to/MyCheckBoxComponent.vue';

storiesOf('Forms|CheckBoxes', module)
  .addDecorator(withKnobs)
  .add('Default', () => ({
    components: { CheckBox },
    props: {
      isChecked: { type: Boolean, default: boolean('checked', true) },
      color: { type: String, default: text('color', '#e64a19') },
      className: { type: String, default: text('class', '') },
    },
    template: `<check-box :class="className" :checked="isChecked" :color="color"></check-box>`,
  }));

I mentioned that I was using withKnobs as a global decorator. I came across this issue that helped me identify the problem.

https://github.com/storybooks/storybook/issues/4947

It turns out that the issue exists when attempting to use the knobs addon alongside storybook-addon-vue-info . If I remove that decorator, the knobs addon works as expected. Hopefully this saves someone else some time. It took me a while to figure that out.

Another pattern that broke in >=4.0.8 is using knobs as computed properties.

<template>
  <square-thumbnail
    :circle="circle"
  />
</template>

<script>
import { boolean } from '@storybook/addon-knobs';
import SquareThumbnail from '../../../common/vue/work/square-thumbnail';

export default {
  components: { SquareThumbnail },
  computed: {
    circle: () => boolean('circle', false)
  }
};
</script>

I presume this is because the instance is preserved, and because Vue isn't aware the knobs aren't pure functions, they are not recomputed. This probably should be documented as well, and I should tell my coworkers to fix this 馃槅

If I understand, work with storybook + VueJS + Knob is not really possible for now ?

@throrin19 actually it鈥檚 possible! I managed to use knobs by declaring them as props. That should works!

If even like this, it still doesn鈥檛 work, could be related to storybook-addon-vue-info as mentioned by @jlpospisil and in that case try to look for a possible workaround in #4947

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

Is there any development in this area? Is there something that I could help somehow to get it fixed?

This exact comment got things working for me as they do in the docs and as they did before 4.0.8: https://github.com/storybooks/storybook/issues/4947#issuecomment-448001928

4.0.2 seems to be the last time things were working fully as expected. Knobs were all kinds of screwy in versions after this. For example, when I would navigate between stories in the lefthand pane, the knobs panel would go blank. I experienced this with 4.2, 4.11, and 5-beta.

Hope some of that helps.

I must say it still doesn't work with 5.0.0-rc.3. I don't have any global decorators or additional add ons.

I'm going to close this as a duplicate to #4947. Let's continue the conversation there and get this sorted out. Thanks for your patience everyone!

Was this page helpful?
0 / 5 - 0 ratings