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