javascript – Storybook: how to update i18 locale depending on globals – Code Utility

[

I try to migrate from @storybook/addon-knobs to @storybook/addon-controls and I’ve got an issue.

I have a knob that I use to update i18n locale.
It also switch from rtl to ltr.
It works flawlessly:

import { select } from '@storybook/addon-knobs'
import Vue from "vue";

// import vue plugins
import VueI18n from "vue-i18n";

// import language file
const message = require("./translations.json");

// i18n and store
Vue.use(VueI18n);

import store from "../src/store";
addDecorator(() => ({
  template: "<story/>",
  i18n: new VueI18n({
    defaultLocale: 'en',
    locale: 'en',
    locales: [ 'en', 'ar' ],
    messages: {
      en: message.en,
      ar: message.ar,
    },
  }),
  // add a props to toggle language
  props: {
    storybookLocale: {
      type: String,
      default: select('I18n locale', ['en', 'ar'], 'en'),
    },
  },
  watch: {
    // add a watcher to toggle language
    storybookLocale: {
      handler() {
        this.$i18n.locale = this.storybookLocale;
        let dir = this.storybookLocale === 'ar' ? 'rtl' : 'ltr';
        document.querySelector('html').setAttribute('dir', dir);
      },
      immediate: true,
    },
  },
}));

Now, when I try to use @storybook/addon-controls, I fail to understand how to do it.

I’ve read Storybook documentation and was able to remove my knob to add a new select in the toolbar.

export const globalTypes = {
  storybookLocale: {
    name: 'storybookLocale',
    description: 'Internationalization locale',
    defaultValue: 'en',
    toolbar: {
      icon: 'globe',
      items: [
        { value: 'en', right: 'πŸ‡ΊπŸ‡Έ', title: 'English' },
        { value: 'ar', right: 'πŸ‡¦πŸ‡ͺ', title: 'Arabic' },
      ],
    },
  },
};

Here is an exemple of a story:

import SectionTitle from '../src/components/onboarding/section-title.vue'

export default {
  title: 'Onboarding/Components/Title',
  component: SectionTitle,
};

const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { SectionTitle },
  template: '<SectionTitle v-bind="$props" />',
});

export const Title:any = Template.bind({});
Title.args = {
  stepNumber: 1,
}

I don’t know how to watch for this global change to update my i18n and the language direction.
In the doc, the global is consumed within a story, but I want it to be global.

Any help would be appreciated.

,

I’m more familiar with React, but I think something along these lines should work.

Solution

  • Listen to context.globals passed as the second parameter to the decorator.
  • Replace addDecorator with the exported decorators array.
  • Update this.storybookLocale to
    context.globals.storybookLocale.

Notes

  • Docs explain how to use a decorator for globals here.
  • I found this related article, but ultimately didn’t gain much from it given that you already had the basic set up. They do go through some additional steps, but I’m not sure they’re necessary in your case.

.storybook/preview.js

import Vue from "vue";
import VueI18n from "vue-i18n";
import store from "../src/store";
import message from "./translations.json";

Vue.use(VueI18n);

export const globalTypes = {
  storybookLocale: {
    name: 'storybookLocale',
    description: 'Internationalization locale',
    defaultValue: 'en',
    toolbar: {
      icon: 'globe',
      items: [
        { value: 'en', right: 'πŸ‡ΊπŸ‡Έ', title: 'English' },
        { value: 'ar', right: 'πŸ‡¦πŸ‡ͺ', title: 'Arabic' },
      ],
    },
  },
};

export const decorators = [
  (story, context) => ({
    template: "<story/>",
    i18n: new VueI18n({
      defaultLocale: 'en',
      locale: 'en',
      locales: [ 'en', 'ar' ],
      messages: {
        en: message.en,
        ar: message.ar,
      },
    }),
    props: {
      storybookLocale: {
        type: String,
        default: 'en',
      },
    },
    watch: {
      storybookLocale: {
        handler() {
          this.$i18n.locale = context.globals.storybookLocale;
          let dir = storybookLocale === 'ar' ? 'rtl' : 'ltr';
          document.querySelector('html').setAttribute('dir', dir);
        },
        immediate: true,
      },
    },
  })
];

]