programing

Vuex 스토어를 Storybook으로 작동시키려면 어떻게 해야 하나요?

randomtip 2022. 8. 10. 19:50
반응형

Vuex 스토어를 Storybook으로 작동시키려면 어떻게 해야 하나요?

Vuex 스토어에서 ACTION에 의해 실행되는 API 호출이 필요한 컴포넌트 스토어가 있습니다.그러나 스토어는 Storybook에서 찾을 수 없습니다. Unhandled promise rejection TypeError: "this.$store is undefined".

이 스토어에 접속하기 위해created그리고.mountedVue 라이프 사이클 훅이 각각 반환되었습니다.undefined.

앱 내에서 Vuex 스토어가 올바르게 작동하고 있습니다.

나는 이야기책을 달린다.5.0.1및 vuex3.1.1.

여기 내 이야기책이야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);

제 컴포넌트의 이야기는 다음과 같습니다.

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" />',
    };
  });

내 컴포넌트는 다음과 같습니다.

<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>

아마 당신 앱 어딘가에 있을 거예요.main.js다음과 같은 작업을 수행합니다.

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

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

그리고 Vue 앱을 만들 때 당신의 소명은new Vue({store, i18n...}).

이미 'i18n' 모듈을 탑재한 Vue를 만들고 있습니다.config.jsVuex와 그 가게도 수입해야 합니다.


스토어를 스토리북 설정으로 Import 또는 조롱해야 하는 것은 컴포넌트가 너무 크거나 스토어와 너무 결합되어 있는 냄새일 수 있습니다.

보통 스토리북은 물건을 표시하는 컴포넌트(폼 컨트롤, 물건 목록...)를 표시하기 위한 것입니다.) 전용 기능을 갖추고 있습니다.이러한 컴포넌트는 보통 소품이나 이벤트를 통해 어플리케이션의 나머지 부분과 통신합니다.이것을 프레젠테이션 컴포넌트라고 부릅니다.

반대로 스토어와 통신하는 컴포넌트는 보통 뷰 또는 페이지이며 상태를 조정하고 백엔드와 대화하며 데이터를 제공합니다.

스토리북 쇼케이스에는 프레젠테이션 컴포넌트만 전시하고 글로벌 모듈에 대해서는 언급하지 않는 것이 좋다고 생각합니다.적어도 나는 이것이 스토리북의 배후에 있는 정신과 주로 사용되는 방식이라고 생각한다.스토어북에서 스토어를 조롱하는 방법에 대한 문서를 많이 찾을 수 없기 때문일 것입니다.스토리북 프로젝트는 보통 처음부터 vuex와 연결되지 않습니다.

스토리에서 새로운 스토어 사례를 전하다(또는 조롱하다)

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: ... 
        }
      }
    };
  });

Nuxt.js 를 사용하고 있는 경우는, 다음의 순서에 따릅니다.

./storybook/store.http

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

그리고 당신의 이야기:

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

export default {
    title: 'MyComponent'
};

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

데코레이터를 써보세요.

import { createStore } from 'vuex';

const _vue = require("@storybook/vue3");
const _addons = require("@storybook/addons");

const withVueRouter = function withVueRouter() {
  const store = arguments?.[0] || createStore({ state: {} });
  return _addons.makeDecorator({
    name: 'withStore',
    parameterName: 'withStore',
    wrapper: (storyFn, context) => {
      _vue.app.use(store);
      return storyFn(context);
    }
  });
};

export default withVueRouter;

사용.

import withStore from '../../../config/storybook/decorators/withStore';
import { createStore } from 'vuex';

const store = createStore({
  state: {
    film: films[0],
  },
});

export default {
  title: 'film-details/FilmDetails',
  decorators: [withStore(store)]
};

const FilmDetailsTemplate = (args) => ({
  components: { FilmDetails },
  template: '<FilmDetails/>',
});

export const template = FilmDetailsTemplate.bind({
});

솔루션을 찾고 있는 경우.mdx스토리 파일의 타입을 선택하면, 다음과 같이 스토어의 동작을 시뮬레이트 할 수 있습니다(Namesthed Store Configuration을 사용합니다).

<!-- SomeComponent.stories.mdx -->

import Vuex from 'vuex';

[...]

export const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { SomeComponent },
  store: new Vuex.Store({
    modules: {
      auth: {
        namespaced: true,
        state: {
          user: {
            id: 20,
            avatar: "/images/avatar.png",
            name: "John Doe",
            login: "jonh.d",
          }
        },
        getters: {
          userPublicData: () => {
            return {
              id: 20,
              avatar: "/images/avatar.png",
              name: "John Doe",
              login: "jonh.d",
            };
          },
        }
      },
    },
  }),
  template: `
    <SomeComponentv-bind="$props" />
  `,
});

<Canvas>
  <Story
    name="Basic"
    args={{
    }}>
    {Template.bind({})}
  </Story>
</Canvas>

언급URL : https://stackoverflow.com/questions/56682493/how-can-i-make-vuex-store-work-with-storybook

반응형