Shopware addon for Storybook

View on Github

Storybook addon for Shopware

The addon provides helpers and loaders to integrate the Shopware 6 administration with Storybook. It contains multiple parts - a loader, an addon panel and a renderer.

The loader is based on VueDocgenApi which is a toolkit to extract informations like events, slots, methods, properties etc. from Vue components. The loader itself transforms the JavaScript files from Shopware in export default to work together with VueDocgenApi.

The provided renderer allows to easily create stories in Storybook using Shopware components. It covers all the necessary parts to write efficent documentation without the need of any boilerplate code.

Last but not least the addon comes with an addon panel which displays the source code of all available Twig blocks inside a component. This way you'll find exactly the part of the code you want to override in your code to provide additional functionality.

The renderer

The renderer is the heart of the addon. It provides methods to create a default story, provide additional stories for the different variants of a component and allows to easily customize the template used for the story itself.

How to use the renderer

The renderer can easily be imported in your story using the following code snippet:

import { getRenderer } from '@shopware-ag/storybook-addon-shopware/renderer';

The function getRenderer accepts a configuration object for the component:

Option Description Optional
title Provides the name and the navigation structure of the component
component Main Vue.js component for all story variants within the story file
additionalComponents Additional Vue.js components which are used within the main Vue.js component. If the main component extends another component, please provide it here.
template Template literal with the HTML markup to render the story. Default: <[component-name] v-bind="$props"></[component-name]>
figmaUrl URL to a figma design which will be displayed in the Design tab

The method getRenderer returns an object with the following methods:

Method Description Arguments
getDefaultStory() Provides the default story which is mandatory in Storybook. none
getStory() Provides the story which gets displayed to the user and shows of a certain variant / combination of properties of the component. args - Preconfigured properties of the componenttemplate - a different template for the component. Useful to document slots and their behavior
getTemplate() Returns the configured template instance from the method getRenderer none
getCustomTemplate() Helper method which provides a template instance to use as the second argument of the method getStory() template - template literal of the template

The following example shows the minimal set of arguments provided to method getRenderer():

import { getRenderer } from '@shopware-ag/storybook-addon-shopware/renderer';
import Icon from 'src/app/component/base/sw-icon';

const { getDefaultStory, getStory } = getRenderer({
    title: 'Basic / sw-icon',
    component: Icon,
});

Writing stories

First and foremost Storybook requires an export default with a default story. The default story needs to contain the title of the story, the necessary component as well as additional components if necessary. We can use the method getDefaultStory which is getting returned from getRenderer to provide the default story:

export default getDefaultStory();

Next up we can write the stories using the getStory method. The easiest way to use the method is export a named constant and provide the return value from getStory() as the value:

export const Default = getStory();

Usually this isn't quite enough, we would like to provide preconfigured properties which we can provide as the first argument of the method:

export const Default = getStory({
    name: 'default-chart-sales',
    color: '#4DC6E9',
});

Storybook recommends the use of UpperCamelCase for your story exports. For example VerticalTabs will be rendered as Virtual Tabs in the navigation structure.

Writing multiple stories

When we're writing multiple stories for a component we usually want to re-use configured properties of a previous story and just add new properties or modify existing properties here. This can be easiliy done using the rest operator in JavaScript.

export const Color = getStory({
    color: '#A092F0',
    firstName: 'John',
    lastName: 'Doe',
    size: '48px',
});

export const Square = getStory({ ...Color.args, ...{ variant: 'square' } });

export const Image = getStory({ ...Color.args,
...{
    imageUrl: 'https://randomuser.me/api/portraits/women/68.jpg',
} });

Using a custom template

Vue.js is heavily relying on slots for content distribution. Using the method getCustomTemplate() we can provide a custom template:

import { getRenderer } from '@shopware-ag/storybook-addon-shopware/renderer';

import Icon from 'src/app/component/base/sw-icon';
import Button from 'src/app/component/base/sw-button';
import EmptyState from 'src/app/component/base/sw-empty-state';

const { getDefaultStory, getStory, getCustomTemplate } = getRenderer({
    title: 'Basic / sw-empty-state',
    component: EmptyState,
    additionalComponents: [
        Icon,
        Button,
    ],
});

export default getDefaultStory();

export const Default = getStory({
    color: '#F88962',
    title: 'No experiment found',
    subline: `Lorem ipsum dolor sit amet, consectetur adipisicing elit. Atque consequatur consequuntur debitis dolore 
            dolorum earum exercitationem expedita ipsam iste iusto officia, officiis quae ratione recusandae reiciendis.
            Earum, ipsam, rerum? Earum.`,
    icon: 'default-object-lab-flask',
    emptyModule: true,
});

export const AdditionalActionButtons = getStory({ ...Default.args, ...{ color: '#57D9A3' } }, getCustomTemplate(`
    <sw-empty-state v-bind="$props">
        <template #actions>
            <sw-button>Click me!</sw-button>
        </template>    
    </sw-empty-state>
`));

Using automatic property bindings in template literals:

When you're writing a template to use within a story you can use the power of VueDocgenApi. Provide the markup the way you want and just make sure you're providing the automatic property bindings using v-bind like in the following example:

<sw-button v-bind="$props"></sw-button>

Providing additional controls which are not defined in a component

Additionally it's possible to provide additional controls for your story to customize the behavior of the component even more. In the following example we're providing an additional control called content which is filling the default slot of the sw-card component:

const { getDefaultStory, getStory } = getRenderer({
    title: 'Basic / sw-card',
    component: Card,
    additionalComponents: [
        Loader,
    ],
    template: `<sw-card v-bind="$props">{{ content }}</sw-card>`,
});

export default getDefaultStory();

export const CardWithTitle = getStory({
    title: 'Example card',
    content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. A aliquam asperiores aut consectetur cum cumque'
});
Made by
  • raoulshopware
    raoulshopware
  • pweyck
    pweyck
  • schuchi
    schuchi
  • klarstil
    klarstil
  • jannisleifeld
    jannisleifeld
  • raknison
    raknison
Tags