storybook: @storybook/vue@4.0.8 and @storybook/addon-knobs@4.0.8 do not update components using JSX render functions in stories

Describe the bug Knobs no longer update components when using JSX render functions in stories. I managed to get it working for standard string templates, but I have a large project using JSX for stories.

To Reproduce Steps to reproduce the behavior:

  1. git clone https://gitlab.com/alexkcollier/storybook-vue-knobs-issue.git
  2. npm install
  3. npm start
  4. Change a knob

Expected behavior Component in story should updates, but won’t unless you rollback storybook and addons to 4.0.7

Screenshots Note how knobs do not match component state. image

Code snippets

// SButton.stories.jsx

import SButton from './SButton.vue'
import { boolean, select, text, withKnobs } from '@storybook/addon-knobs'
import { storiesOf } from '@storybook/vue'

storiesOf('SButton JSX', module)
  .addDecorator(withKnobs)
  .add('Button JSX', () => {
    const buttonColor = select('Button color', ['green', 'red'], 'green', 'Optional Props')
    const buttonSize = select(
      'Button size',
      ['small', 'regular', 'large'],
      'small',
      'Optional Props'
    )
    const buttonText = text('Button text', 'Sample text', 'Slots')

    const props = {
      buttonColor,
      buttonSize
    }

    const disabled = boolean('disabled', false, '$attrs')

    return {
      render: h => (
        <div>
          <SButton {...{ props }} disabled={disabled}>
            {buttonText}
          </SButton>
          <p>{buttonColor}</p>
          <p>{buttonSize}</p>
        </div>
      )
    }
  })
// SButton.vue

<template>
  <button v-bind="$attrs" :class="classList" class="button"><slot /></button>
</template>

<script>
export default {
  name: 'SButton',

  props: {
    buttonColor: {
      type: String,
      default: ''
    },

    buttonSize: {
      type: String,
      default: ''
    }
  },

  computed: {
    classList() {
      return [
        // Handles storybook default
        this.buttonColor ? `button--color-${this.buttonColor}` : 'button--color-green',
        this.buttonSize ? `button--size-${this.buttonSize}` : ''
      ]
    }
  }
}
</script>

<style>
.button--color-green {
  background-color: green;
}
.button--color-red {
  background-color: red;
}

.button--size-large {
  font-size: 3rem;
}
.button--size-regular {
  font-size: 1rem;
}
.button--size-small {
  font-size: 0.5rem;
}
</style>

System:

  • OS: Windows 10
  • Device: Desktop
  • Browser: Chrome
  • Framework: Vue, project configured using vue-cli
  • Addons: @storybook/addon-knobs
  • Version: 4.0.8 .

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 2
  • Comments: 23 (14 by maintainers)

Most upvoted comments

you should have an invite to a repo. thank you for your help 😃

Edit: If you go to storybook-demo you can see the same behaviour. When you click on the section knobs and then on “All Knobs” you will see that the component will not react to the changes of knobs. Just try to alter the name for example.

… sry i missed the “public” … here it is: StorybookTest-Repo

@y-nk Thank you for your answer. Unfortunately the way you wrote the story, does not work. I think what you’ve wanted to write is:

storiesOf(TestComponent.name, module)
  .addDecorator(withKnobs)
  .add('as Component', () => ({
    components: { TestComponent },
    props: {
      textProp: {
        type: String,
        default: text('text', 'peter'),
      },
    },
    template: `<TestComponent :text.sync="textProp" />`,
  }))

But still it never updates the component, when I change the value of the knob. Is this working as intended?

@dasdeck Your template should be <BuilderStory :user="userInput()" />. You can also just set userInput.default to user, rather than the function.

@y-nk I refactored to your suggested implementation on 4.0.11 and ran into a new issue . Wrapping stories in a global decorator stops the knobs from being reactive.

@Laslo89 i’m glad i could be of help. I couldn’t see your webpack html loader error when i ran your repo, so feel free to let me know ; it could be a side effect related to our update somehow.

We’ll update you about this bug/issue whenever possible.

@Laslo89 sorry for my late response.


First : About the non-working “all knobs” demo :

It’s actually normal that way. I think was kindof a false-positive working feature in my opinion as I’m not convinced that this way of coding is a best practice regarding Vue components.

If you look at the source code of the story, then you can see that the story component is a simple { template: String } object, which template is a pure static string (because all the knobs are injected in the string from the backquotes). The direct consequence of that is that the story component cannot mutate ; it will be destroyed and recreated every single time you modify a knob and then all the internal changes and css animations you could have running will be destroyed/reset. You can see this behavior here. The patch I’ve been pushing prevents this by sending new prop values instead ; here’s an example about it (following the previous one). That way, even if you use sub components (like in your TestComponent story), they will not be re-created at render ( non-working demo / working demo ).

I will update the examples asap so this practice won’t be encouraged by storybook anymore.


On a more practical way, I found your bug. It actually comes from here : https://github.com/Laslo89/StorybookTest/blob/master/.storybook/config.js#L19-L22

Because of this wrapper, the render functions receives the vuetify app component instead of the actual story. I believe storybook should support this, and I’m working with @igor-dv to provide the best fix in no time.

In the meantime, in order not to let you blocked, if you only used this wrapper for providing the store to your component, you can comment this wrapper and provide the store directly in your stories.

@Laslo89 i’d like to offer more help. is there any way to isolate your trial into a public repo which i could fork ?

We made changes with knobs when handling Vue.

As you may know, Vue renders differently from React.

The way promoted before in storybook was using knobs as fixed values in template, or as values bound to the internal state of the story component (data). The direct consequence is that the story component was destroyed and recreated each and every time a knob was updated (easily spottable with a console.log in created() and/or destroyed())

As for now, with @igor-dv we proposed to migrate this behavior to preserve the component instance and prefer the use of knobs as props, as it should be (since knobs are external values injected into your component - just what props are).