How can I make Vuex store work with Storybook?

Solution 1:

I bet somewhere in your app, probably main.js, you're doing something like:

import Vuex from 'vuex';
Vue.use(Vuex);

const store = new Vuex.Store({
  state,
  mutations,
  getters,
});

And then, when creating the Vue app, your calling new Vue({store, i18n...}).

You're already forging Vue with the ' i18n' module in your config.js. You would need to import Vuex and the store there too.


Now, having to import your store -or mock it- in your storybook setup may be a smell of your components being too large, or being too coupled with your store.

Usually, storybook is more intended to show components that display stuff (form controls, list of things... ) that have a dedicated functionality. Such components usually communicate with the rest of your application via props and events. Let's call this presentational components.

On the contrary, components that communicates with a store are usually views or pages, and they orchestrate the state and talk with the backend, and supply data to the former.

I think you should display on the storybook showcase only presentational components, and avoid talking global modules within them. At least, I believe this is the spirit behind storybook and how it is mainly used. That may be the reason because you don't find much docs about how to mock your store in storybook: storybook projects usually don't connect to vuex in the first place, I think.

Solution 2:

pass new store instance (or mocking) in story

import Vuex from "vuex";
import { storiesOf } from '@storybook/vue';
import { withReadme } from 'storybook-readme';
import { withKnobs } from '@storybook/addon-knobs';
import HandoffMainView from './HandoffMainView.vue';
import readme from './README.md';

storiesOf('HandoffMainView', module)
  .addDecorator(withReadme([readme]))
  .addDecorator(withKnobs)
  .add('Default', () => {
    /* eslint-disable */
    return {
      components: { HandoffMainView },
      data() {
        return {
          isLoading: true,
          component: {
            src: '',
            data: [],
          },
        };
      },
      template: '<handoff-main-view :component="component" />',
      store: new Vuex.Store({ // here
        modules: {
          namespaced: true,
          actions: ... 
        }
      }
    };
  });

Solution 3:

If you are using Nuxt.js, here is how you can do it:

./storybook/store.js

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const store = new Vuex.Store({
    state: require("../store/index.js").state,
    getters: require("../store/index.js").getters,
    actions: require("../store/index.js").actions,
    mutations: require("../store/index.js").mutations,

    modules: {
        ads: {
            namespaced: true,
            state: require("../store/ads.js").state,
            getters: require("../store/ads.js").getters,
            actions: require("../store/ads.js").actions,
            mutations: require("../store/ads.js").mutations
        },

        features: {
            namespaced: true,
            state: require("../store/features.js").state,
            getters: require("../store/features.js").getters,
            actions: require("../store/features.js").actions,
            mutations: require("../store/features.js").mutations
        },


        user: {
            namespaced: true,
            state: require("../store/user.js").state,
            getters: require("../store/user.js").getters,
            actions: require("../store/user.js").actions,
            mutations: require("../store/user.js").mutations
        },
    }
});

export default store

Then in your story:

// ...
import store from '@/.storybook/store';

export default {
    title: 'MyComponent'
};

export const MyComponentStory = () => ({
    store: store,
    // ...
})