Learnings from using Nuxt and Storybook at scale

DX IS THE NEW BLACK

Who am I?

Javascript
freelance

MTG addict

Aurélie VIOLETTE

International e-commerce platform from scratch

Better performance

Easy deployment

Shallow learning curve

Don't forget dx

Bad projects teach you a lot

Aurélie.V, Jan 2019

build the empire

Documentation

Devtools

Mocks

Unit tests

Design system

CI/CD

what is storybook

what is storybook

Living documentation

Visually driven developement

What is storybook (technically)

Main UX
=
React

Preview
=
Iframe

Channel
=
Events

What is storybook (technically)

import Accordion from './Accordion'

export default {
  title: 'Components/Utils/Accordion',
  component: Accordion,
}

export const BasicExample = () => ({
  components: { Accordion },
  data() {
    return {
      items: Array(5)
        .fill(0)
        .map((_, index) => index),
    }
  },
  template: `
      <Accordion :items="items">
        <template v-slot:button="{ index, isExpanded }">Item {{ index }} ({{ isExpanded ? 'Opened' : 'Closed' }})</template>
        <template v-slot:content="{ index }">Content of item {{ index }}</template>
      </Accordion>
  `,
})

addons

Customize Storybook for your project

Bring "Nuxt" logic

Bring business logic

Easy to do

Decorator

Wrap
story

Inside
preview

Locally
or
Globally

Decorator (basic example)

import { addDecorator } from '@storybook/vue'
import AccessibilityWrapper from '~/components/AccessibilityWrapper'

addDecorator(storyFn => ({
  components: { AccessibilityWrapper },
  template: `<AccessibilityWrapper><story /></AccessibilityWrapper>`,
}))

Decorator (complex example)

import addons, { makeDecorator } from '@storybook/addons'
import { STORY_CHANGED } from '@storybook/core-events'

export const withStore = makeDecorator({
  name: 'withStore',
  parameterName: 'store',
  skipIfNoParametersOrOptions: false,
  wrapper: (getStory, context, { parameters = {} }) => {
    const { modules = {} } = parameters
    return {
      created() {
        for (name in modules) {
          this.$store.registerModule(name, modules[name])
        }
        const channel = addons.getChannel()
        channel.on(STORY_CHANGED, () => {
          for (name in modules) {
            this.$store.unregisterModule(name)
          }
        })
      },
      template: '<story></story>',
    }
  },
})
// preview.js
import { addDecorator } from '@storybook/vue'
import { withStore } from './addons/store'

addDecorator(withStore)
// Inside story
export const FrenchFooter = () => ({
  components: { Footer },
  template: '<Footer />',
})
FrenchFooter.story = {
  parameters: {
    store: {
      modules: {
        footer: footerStoreMock({ lang: 'fra-fr' }),
      },
    },
  },
}

custom tools

React components

Specific slot

Many components and hooks available

custom tools (example)

custom tools (Example)

import { IconButton } from '@storybook/components'
import { styled } from '@storybook/theming'
import { useChannel, useAddonState } from '@storybook/api'

const ButtonLabel = styled.div(({ theme }) => ({
  fontSize: theme.typography.size.s2 - 1,
}))

export const TextDirectionTool = () => {
  const [state, setState] = useAddonState(`${ADDON_ID}-text-direction`, 'ltr')
  const emit = useChannel()

  function toggleTextDirection() {
    const newState = state === 'ltr' ? 'rtl' : 'ltr'
    setState(newState)
    emit(TEXT_DIRECTION_CHANGED, newState)
  }

  return (
    <IconButton onClick={toggleTextDirection}>
      <ButtonLabel>{state.toUpperCase()}</ButtonLabel>
    </IconButton>
  )
}
// register.js

addons.register(`${ADDON_ID}`, api => {
  addons.add(`${ADDON_ID}-text-direction`, {
    title: 'text-direction switch',
    type: types.TOOL,
    match: ({ viewMode }) => viewMode === 'story',
    render: () => <TextDirectionTool />,
  })
})
// preview.js

const channel = addons.getChannel()
channel.on(TEXT_DIRECTION_CHANGED, direction => {
  store.commit('setLocaleContext', { direction })
  document.dir = direction
})

possibilities are infinite

Different
pieces of UI
(tab, panel, ...)

Community addons

Rich API

challenges

Duplicate
build

Nuxt
pages

Third
parties

why it's cool

Process

Involves
everyone

Feedback earlier

conclusion

Bringing joy to your dev is a sure path to success

Aurélie.V, Fév 2020

thanks

@purple_orwel

If you want more information

DX is the new black

By Aurélie Violette

DX is the new black

FrontEndLove Amsterdam, 13th February 2020

  • 1,176