📘

【2021-8】 Next.js + TypeScript + Storybook(Atomic Design) + Sass + jest

11 min read

フロントエンド環境に実装をしたStorybookに、Sassを導入する

StorybookへのSassの導入でcss-loaderでエラーにはまり、手順の記事を書きました。
原因はWebpack4と5で破壊的な変更(更新)からPKGをインストールして解決できました。(めでたし)

でも、フロントエンド開発に必要そうなベーシックな環境構築を、「ざーーっと、まとめたかった」ことが、個人的には一番の記事の動機です。

実装リスト

  • docker
  • Next.js/TypeScript
  • Sass
  • Storybook
  • jest
  • Atomic design

※導入は個人の責任でおねがいします。(お約束文)


開発情報

  • Macbook MacOS BigSur 11系
  • docker ※PCのローカル環境は汚したくない
  • node v16.3.0 ※v14系ではない(わりとポイントかもしれません)

docker-compose ※今後、バックエンド側も実装する予定です(今後の別記事に書くかも)


初期のディレクトリ構成(最初の状態だけ画像を貼ります)

  1. docker-compose.yamlファイルをルートディレクトリ直下に作成
  2. dockerディレクトリの直下に、test_frontendディレクトリを作成して、Dockerfileを作成

フロー

  1. Docker環境構築
  2. Next.js + Typescriptの導入
  3. storybookの導入
  4. jestの導入
  5. Sassの導入
  6. StorybookにSassを導入

Step_1 Docker環境構築

Dockerfileの作成

最小限をインストールします。

FROM node:16.3-alpine

RUN yarn install && yarn global add \
    create-react-app

WORKDIR /test_frontend

docker-compose.yamlの作成

Next.jsとStorybookのport番号はお好きなもので
ディレクトリもお好きに

version: '3.8'
services:
  test_frontend:
    container_name: next_storybook_jest_container
    build: ./docker/test_frontend
    ports:
      - 13030:3000
      - 16036:6006
    volumes:
      - ./test_frontend:/test_frontend
    stdin_open: true
    tty: true
    environment:
      - TZ=Asia/Tokyo
    networks:
      - default

networks:
  default:

dockerをbuildする

確認用コマンド(参考)

docker images -a
docker ps -a
docker-compose ps -a
  1. docker-compose build
docker-compose build --no-cache

--no-cacheは念のため

  1. docker-compose up -d
docker-compose up -d
  1. アップしてるかチェックをする
docker-compose ps -a


こんな形で、あとはルートディレクトリ直下に、test_frontendの空ディレクトリができてればOK

docker、docker-composeのどちらでもお好きなようにcontainerに接続ください。
以下はdockerコマンド(参考)

docker exec -it next_storybook_jest_container sh

Step_2 Next.js + Typescriptの導入

起動したdocker contianerに接続をして、早速、create-next-appをします。

Next.jsのインストール

npx create-next-app .

TypeScriptのインストール

  1. tsconfig.jsonファイルを作成する
touch tsconfig.json
  1. PKGをインストールする
yarn add -D typescript @types/react @types/node
  1. インストールできたら起動する
yarn dev

ローカルからサイト参照ができたらOK

prettierの設定

しわすれるので先に作成をします。※設定はお好みで

  1. .prettierrc ファイルを作成する
touch .prettierrc
  1. .prettierrcを編集する
    {
        "singleQuote": true,
        "semi": false
    }

Next.jsのファイル編集

  1. pages/_app.jsと、pages/index.jsのファイル拡張子を.tsxに変更する

pages/_app.js

mv pages/_app.js pages/_app.tsx

pages/index.js

mv pages/index.js pages/index.tsx
  1. pages/_app.tsxを編集する
import { AppProps } from 'next/app'
import '../styles/globals.css'

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
} 
  1. pages/index.jsを編集する

不要なコードを削除するのと、後ほど、CSS/Sassの動作テスト用に、classNameを付与したbuttonタグをコーディングしています。

const Home: React.FC = () => {
  return (
    <>
      <h1>Next</h1>
      <button className="sample" >sample</button>
    </>
  )
}

export default Home

Step_3 Storybookの導入

  1. npx sb init

yarn global add create-react-appがすんでいるため、コマンドを実行できます。(参考)

npx sb init

ファイルが作成されますが、以降の手順でAtomic Designのディレクトリを構築していきます。

Atomic Designの導入

※本記事の構成は参考です。(今回、atomsしか使いません)

  1. 各ディレクトリを作成する。
mkdir assets
mkdir -p components/atoms
mkdir components/atoms/types
mkdir components/molecules
mkdir components/organisms
mkdir components/templates

typesフォルダで、TypeScriptの型ファイルをそこで管理します。

  1. npx sb init で作成されたファイルをテスト用で一部だけ移動する。
mv stories/button.css components/atoms
mv stories/Button.stories.tsx components/atoms
mv stories/Button.tsx components/atoms
touch components/atoms/types/Button.types.ts
  1. components/atoms/types/Button.types.tsを編集する。
export interface ButtonProps {
  primary?: boolean;
  backgroundColor?: string;
  size?: 'small' | 'medium' | 'large';
  label: string;
  onClick?: () => void;
}
  1. components/atoms/Button.tsx を編集する。

なお、もともとコーディングされてあった、interface ButtonPropsは削除してください。

./types/Button.typesをインポートする

import { ButtonProps } from './types/Button.types'
  1. storiesディレクトリはいらないので削除する
rm -r stories

.storybook/main.jsの編集

.storybook/main.jsを以下へ修正します。
なお、Sassはあとでいれてくので、CSSのまま進めます。

module.exports = {
  "stories": [
    "../components/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
  ],
}

Storybookの起動

起動の前に、package.jsonのscriptsを編集します。

    "scripts": {
        <省略>
        "storybook": "start-storybook -s public -p 6006"
    },
yarn storybook

ローカルポートよりアクセスする


Step_4 jestの導入

PKGをインストール

各PKGの解説は省きます。

yarn add -D setimmediate next-page-tester jest babel-jest @babel/core @testing-library/react @types/jest @testing-library/jest-dom @testing-library/dom @testing-library/user-event jest-css-modules

ついでではないですが、開発に他PKGもインストールします。

yarn add axios msw swr

.babelrcファイルの作成

  1. .babelrcファイルを作成する
touch .babelrc
  1. .babelrcファイルを編集する
 {
    "presets": ["next/babel"]
 }

packege.jsonへjestの設定

こちら参考にどうぞ moduleNameMapperの参考

  1. packege.jsonへ、jestを追記する
    "jest": {
        "testPathIgnorePatterns": [
            "<rootDir>/.next/",
            "<rootDir>/node_modules/"
        ],
        "moduleNameMapper": {
            "\\.(css|scss)$": "<rootDir>/node_modules/jest-css-modules"
        }
    }
  1. packege.jsonへ、scriptsを追記する
    "scripts": {
        <省略>
        "test": "jest --env=jsdom --verbose"
    },

jestのテストファイルなどの準備

  1. test用ファイルのディレクトとファイルを作成する
mkdir __test__
touch __test__/Home.test.tsx
  1. 作成したHome.test.tsxファイルを編集する
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
import Home from '../pages/index'

it('Should render title text', () => {
  render(<Home />)
  expect(screen.getByText('sample')).toBeInTheDocument()
})

jestのテスト動作チェック

yarn test

正常にpassしたらOKです。(jestはこれにておわり)


Step_5 Sassの導入

  1. Sassをインストールする

Next.js的にはこれだけで終わりです。

yarn add sass

公式のガイドの通り、以下のパス設定を行います。
以降の、babel-plugin-module-resolverを使うことで同じように動作します。

const path = require('path'); //追加

module.exports = {
  reactStrictMode: true,
  // 以下を追加
  sassOptions: {
    includePaths: [path.join(__dirname, 'styles')],
  },
}
  1. styles/global.scssファイルにテスト用の編集をする
$color_orange: orange;
$base_font_size: 20px;

.sample {
  color: $color_orange;
  font-size: $base_font_size;
}
  1. pages/_app.tsxにimportする
import '../styles/globals.scss'
  1. Sassの動作チェックをする
yarn dev

ローカルからサイト参照ができたらOK

pages/_app.tsxに、確認するためテストでclassNameを付与したbuttonタグを作成しましたね。

Sassが動作してることが確認できたらOKです。


Step_6 StorybookにSassを導入

本題のStorybookへSassを設定していきます。

各Stepは以下の通りです。

  1. sass-loaderインストール
  2. 各PKGのインストール
  3. StorybookがSassファイルを認識するためのパス設定
  4. .storybookディレクトリのファイル編集

PKGの解説(参考情報)

Sassの導入で「あれ?」と思った方はいるかもしれません。
Next.jsの記事で目にする、@zeit/next-sass,node-sassのPKGをインストールしてないからです。(もしかしたら、この結果、ハマる人がおおいのかもしれないです)

記事も長くなりましたから、上記の理由は別の記事:node-sassの終了アナウンスから、dart-sassを使うで解説します

StorybookにSassを導入するためのポイントはStep_2です。

StorybookでSassを動かすため、Webpackの破壊的な更新により(重要)、以下のPKGが必要になります。

@storybook/builder-webpack5
@storybook/manager-webpack5
webpack ←最新ヴァージョンを使うため必要です。

および、Storybookがファイルのパスを認識するために、babel-plugin-module-resolverをインストールする必要があります。

PKGのインストールができてしまえば、あとはファイルを編集するだけですね。(苦労・・・)
css-loader,style-loaderも忘れずに!

1. sass-loaderインストール

まずお約束の、sass-loaderインストール ※10系でないと、storybookが起動しません。

yarn add -D sass-loader@^10

2. 各PKGのインストール

yarn add -D webpack @storybook/builder-webpack5 @storybook/manager-webpack5  css-loader style-loader babel-plugin-module-resolver

3. StorybookがSassを認識するためのパス設定

babel-plugin-module-resolverを使ってパス設定を行います。
  1. .babelrcファイルの編集をする

pluginsを追加します。

"plugins": [
    [
      "module-resolver",
      {
        "root": [
          "."
        ],
        "alias": {
          "@": "./"
        }
      }
    ]
  ]
  1. tsconfig.jsonファイルを編集する

baseUrlとpathsを追記します。

    "baseUrl": ".",
    "paths": {
      "@/":[
        "./*"
      ]
    },

4. .storybookディレクトリのファイル編集

実はStorybookのアドオンの「@storybook/preset-scss」を使えば、Storybook上、scssは動きます。が・・動作エラーばかりに遭遇したため、結局、外しました。アドオンの利用は今後なのかなと思います。

  1. .storybook/main.jsファイルを編集する
const path = require('path');

module.exports = {
  "stories": [
    "../components/**/*.stories.@(js|jsx|ts|tsx)"
  ],
  "addons": [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
  ],
  core: {
    builder: 'webpack5',
  },
  webpackFinal: async (config) => {
    const rootPath = path.resolve(__dirname, '../');
    config.resolve.alias['@'] = rootPath;

    config.module.rules.push({
        test: /\.scss$/,
        sideEffects: true,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              importLoaders: 2,
            },
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      });
    
    return config;
  }
}
  1. .storybook/preview.jsファイルを編集する
import '@/styles/globals.scss'
  1. componets/atoms/button.css の拡張子を.scssへ変更する
mv components/atoms/button.css components/atoms/button.module.scss
  1. components/atoms/button.module.scssのディレクトリを移動する
mv components/atoms/button.module.scss ../../styles/components/atoms
  1. components/atoms/Button.tsxファイルを修正する
# import './button.css'; 削除でOK
import '../../styles/components/atoms/button.module.scss';
  1. components/atoms/Button.tsxに、テスト用でbuttonタグをコーディングしておきます
<button className="sample">sample</button>
  1. styles/components/atoms/button.module.scss にインポートを追記する

変数やmixinを読み込むためのコーディングです。

@import '@/styles/global.scss';

Sassを導入したStorybookを起動する

yarn storybook

ローカルポートよりアクセスする

お疲れさまでした。

エラーなく導入ができますように

Discussion

ログインするとコメントできます