본문 바로가기
매일 해내는 개발/Next.js

Next.js에서 vanilla-extract 사용 시 storybook 설정하는 방법 + 예시 코드

by 해야지 2024. 5. 29.
반응형

vanilla-extract는 linaria에 비해 공식문서가 잘 되어 있고, next용 plug-in도 따로 제공해줘서 어렵지 않게 설정 할 수 있었다.

하지만 Storybook을 적용하려니 또 난관에 부딪혔다.

조금 더 찾아보니 이것도 셋팅이 필요했다!

storybook 공식 문서 > setup에서 vanilla-extract를 셋팅하는 방법을 제공해 주고 있었다.

 

https://storybook.js.org/recipes/@vanilla-extract/css

 

Integrate Vanilla Extract with Storybook | Storybook

Get zero-runtime stylesheets in TypeScript.

storybook.js.org

pnpm 사용 시 다음과 같이 설치를 진행 해주면 된다.

pnpx install -D @vanilla-extract/webpack-plugin mini-css-extract-plugin
pnpx storybook@latest add @storybook/addon-styling-webpack

 

그리고 .storybook 폴더 안에 main.ts 파일을 수정해 주면된다.

./.storybook/main.ts

import type { StorybookConfig } from '@storybook/nextjs';
import * as path from 'path';
import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin';

const config: StorybookConfig = {
  stories: ['../app/**/*.mdx', '../app/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-onboarding',
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@chromatic-com/storybook',
    '@storybook/addon-interactions',
    '@storybook/addon-styling-webpack',
  ],
  framework: {
    name: '@storybook/nextjs',
    options: {},
  },
  staticDirs: ['../public'],
  webpackFinal: async (config) => {
    // Vanilla Extract 로더 추가
    config.module.rules.push({
      test: /\.css\.ts$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: { modules: true },
        },
        'vanilla-extract-loader',
      ],
      include: path.resolve(__dirname, './'),
    });
    config.plugins.push(new VanillaExtractPlugin());

    return config;
  },
};

export default config;

stories에서 확장자를 변경해주고

addon에 `@storybook/addon-styling-webpack`을 추가한다.

그리고 webpackFinal의 내용을 추가해주면 된다.

 

코드 예시

import { Meta, StoryObj } from '@storybook/react';
import { Divider } from '@/shared/styles/design-system/components';
import { DividerProps } from '@/shared/styles/design-system/components/Divider/Divider';

const meta: Meta<typeof Divider> = {
  title: 'Components/Divider',
  component: Divider,
  argTypes: {
    size: { control: 'number' },
    color: { control: 'color' },
    orientation: {
      control: { type: 'radio' },
      options: ['horizontal', 'vertical'],
    },
    isFlexItem: { control: 'boolean' },
  },
};

export default meta;

type Story = StoryObj<DividerProps>;

export const Horizontal: Story = {
  args: {
    size: 1,
    color: '#000',
    orientation: 'horizontal',
    isFlexItem: false,
  },
  render: (args) => (
    <div>
      <p>Above the divider</p>
      <Divider {...args} />
      <p>Below the divider</p>
    </div>
  ),
};

export const Vertical: Story = {
  args: {
    size: 1,
    color: '#000',
    orientation: 'vertical',
    isFlexItem: true,
  },
  render: (args) => (
    <>
      <div style={{ display: 'flex', gap: '16px' }}>
        <div>Left of the divider</div>
        <Divider {...args} />
        <div>Right of the divider</div>
      </div>
      <br />
      <br />
      flex의 item인 vertical divider의 isFlexItem이 true라면, divider를 감싸는 부모 요소가 필요
      없으며 따라서 height도 명시해주지 않아도 된다.
    </>
  ),
};

 

반응형

댓글