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