I have a component story that requires an API call performed by an ACTION from my Vuex store. However, the store can't be found by Storybook: Unhandled promise rejection TypeError: "this.$store is undefined".
I've tried to access the store through the created and mounted Vue lifecycle hooks but each of them returned undefined.
My Vuex store is correctly working inside my app.
I run on storybook 5.0.1 and vuex 3.1.1.
Here's my storybook config.js:
// Taken from https://davidwalsh.name/storybook-nuxt & https://github.com/derekshull/nuxt-starter-kit-v2/blob/master/.storybook/config.js
import { addParameters, configure } from '@storybook/vue';
import { withOptions } from '@storybook/addon-options';
import { setConsoleOptions } from '@storybook/addon-console';
import { create } from '@storybook/theming';
import Vue from 'vue';
import VueI18n from 'vue-i18n';
// Vue plugins
Vue.use(VueI18n);
setConsoleOptions({
panelExclude: [],
});
// Option defaults:
addParameters({
options: {
/**
* show story component as full screen
* @type {Boolean}
*/
isFullScreen: false,
/**
* display panel that shows a list of stories
* @type {Boolean}
*/
showNav: true,
/**
* display panel that shows addon configurations
* @type {Boolean}
*/
showPanel: true,
/**
* where to show the addon panel
* @type {String}
*/
panelPosition: 'bottom',
/**
* sorts stories
* @type {Boolean}
*/
sortStoriesByKind: false,
/**
* regex for finding the hierarchy separator
* @example:
* null - turn off hierarchy
* /\// - split by `/`
* /\./ - split by `.`
* /\/|\./ - split by `/` or `.`
* @type {Regex}
*/
hierarchySeparator: /\/|\./,
/**
* regex for finding the hierarchy root separator
* @example:
* null - turn off multiple hierarchy roots
* /\|/ - split by `|`
* @type {Regex}
*/
hierarchyRootSeparator: /\|/,
/**
* sidebar tree animations
* @type {Boolean}
*/
sidebarAnimations: true,
/**
* enable/disable shortcuts
* @type {Boolean}
*/
enableShortcuts: true,
/**
* theme storybook, see link below
*/
theme: create({
base: 'light',
brandTitle: '',
brandUrl: '',
// To control appearance:
// brandImage: 'http://url.of/some.svg',
}),
},
});
const req = require.context('../src/components', true, /\.story\.js$/)
function loadStories() {
req.keys().forEach((filename) => req(filename))
}
configure(loadStories, module);
Here's my component's story:
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" />',
};
});
Here's my component:
<template>
<main class="o-handoff-main-view">
<div class="o-handoff-main-view__content">
<div
:class="[
'o-handoff-main-view__background',
background ? `o-handoff-main-view__background--${background}` : false
]"
>
<loader
v-if="isLoading"
:color='`black`'
class="o-handoff-main-view__loader"
/>
<div
v-else
class="o-handoff-main-view__ui-component"
:style="getUiComponentStyle"
>
<img
:src="uiComponent.src"
alt=""
>
<handoff-main-view-layer-list
:layers="uiComponent.data"
/>
</div>
</div>
</div>
<div class="o-handoff-main-view__controls">
<handoff-main-view-zoom-handler
:default-zoom-level="zoomLevel"
:on-change="updateZoomLevel"
/>
</div>
</main>
</template>
<script>
import { mapActions } from 'vuex';
import Loader from '../../01-atoms/Loader/Loader.vue';
import HandoffMainViewZoomHandler from '../HandoffMainViewZoomHandler/HandoffMainViewZoomHandler.vue';
import HandoffMainViewLayerList from '../HandoffMainViewLayerList/HandoffMainViewLayerList.vue';
export default {
components: {
Loader,
HandoffMainViewZoomHandler,
HandoffMainViewLayerList,
},
props: {
background: {
type: String,
default: 'damier',
},
component: {
type: Object,
required: true,
},
},
data() {
return {
isLoading: true,
zoomLevel: 1,
uiComponent: {
src: null,
}
};
},
mounted() {
this.setUiComponentImage();
},
methods: {
...mapActions('UiComponent', [
'ACTION_LOAD_SIGNED_URLS'
]),
async setUiComponentImage() {
const uiComponentImg = new Image();
const signedUrls = await this.ACTION_LOAD_SIGNED_URLS([this.component.id]);
uiComponentImg.onload = () => {
this.isLoading = false;
};
uiComponentImg.src = this.uiComponent.src;
},
},
};
</script>