tobiasdiez / storybook-vue-addon

Storybook stories in native Vue format
MIT License
46 stars 1 forks source link

[WIP] Transformation mapping between Storybook CSF -> Vue SFC #86

Closed floroz closed 1 year ago

floroz commented 1 year ago

I am going to provide some real case example to discuss complex Stories' setup and how the same setup would look like using storybook-vue-addon

1. Story inheritance

One of the main benefits of the StoryObj setup is the ability to inherit configuration from the previous Stories.

This also includes being able to reuse the same rendering and just changing a property:

export const Black: Story = {
  args: {
    color: 'black',
  },
  render: args => ({
    setup() {
      return { args }
    },
    template: `
      <MyComponent v-bind="args">
        <MyAdditionalSetup />
      </MyComponent>
    `
  })
};

export const White: Story = {
  ...Black,
  args: {
    color: 'white',
  }
};

1.2 Approach

<script setup>
const SharedSetup = defineComponent({
  props: ['args'],
  template: `
      <MyComponent v-bind="$props.args">
        <MyAdditionalSetup />
      </MyComponent>
  `
})
</script>

<template>
  <Story>
    <template #meta="{ args }">
      <SharedSetup :args="args" />
    </template>
  </Story>
  <Story :args="{ color: 'white' }">
    <SharedSetup :args="args" />
  </Story>
  <template>

1.3 Challenges

<template>
  <Story>
    <template #meta="{ args }">
      <MyComponent v-bind="args">
        <MyAdditionalSetup />
      </MyComponent>
    </template>
  </Story>
  <Story :args="{ color: 'white' }">
    <MyComponent v-bind="args">
        <MyAdditionalSetup />
      </MyComponent>
  </Story>
  <template>

2. Isolated Context: Components depending on provide/inject and Decorators.

export default {
  title: "Components/Breadcrumbs/BreadcrumbsItem",
  component: BBreadcrumbsItem,
} satisfies Meta<typeof BBreadcrumbsItem>;

type Story = StoryObj<typeof BBreadcrumbsItem>;
export const DefaultBlack: Story = {
  decorators: [
    () => ({
      template: `<div class="bg-white"><story /></div>`,
      setup() {
        provide(BREADCRUMB_COLORSCHEME_SYMBOL, 'black');
      },
    }),
  ],
  args: {
    label: "label",
    to: "https://www.labelin.org/",
  },
};

export const DefaultWhite: Story = {
  decorators: [
    () => ({
      template: `<div class="bg-black"><story /></div>`,
      setup() {
        provide(BREADCRUMB_COLORSCHEME_SYMBOL, 'white');
      },
    }),
  ],
  args: {
    label: "label",
    to: "https://www.label.org/",
  },
};

In this example, we have two things to tackle:

2.1 Proposal

<script setup>
// your imports here...

defineMeta<typeof BBreadcrumbsItem>({
  component: BBreadcrumbsItem,
  args: {
    label: "label"
    to: "https://www.labelin.org/"""
  }
})

const DecoratorBlack= defineComponent({
  setup() {
    provide(BREADCRUMB_COLORSCHEME_SYMBOL, "white")
  },
  template: `
    <div class="bg-white">
      <slot/>
    </div>
  `
})

const DecoratorWhite= defineComponent({
  setup() {
    provide(BREADCRUMB_COLORSCHEME_SYMBOL, "white")
  },
  template:`
    <div class="bg-black">
      <slot/>
    </div>
  `
})
</script>

<template>
  <Story :args="{label: 'black'}">
      <template #meta="{args}">
        <DecoratorBlack>
          <BBreadcrumbsItem v-bind="args"/>
        </DecoratorBlack>
      </template>
  </Story>
  <Story :args="{label: 'white'}">
     <template #meta="{args}">
        <DecoratorWhite>
          <BBreadcrumbsItem v-bind="args"/>
        </DecoratorWhite>
      </template>
  </Story>
<template>
floroz commented 1 year ago

submitted early by mistake.