https://www.figma.com/embed?embed_host=notion&url=https%3A%2F%2Fwww.figma.com%2Ffile%2FWQWQdH9JWwcSugQeWIJMxh%2F2023-frontend-basic-react-todoapp%3Ftype%3Ddesign%26node-id%3D5-189%26t%3DHgFfpvctxroAbUVc-4

[お手本 storybook(http://shinonome.io/frontend-basic-todoapp-storybook/?path=/story/atoms-title--default)](https://shinonome.io/frontend-basic-todoapp-storybook/iframe.html?path=/story/atoms-title--default&viewMode=story&full=1&shortcuts=false&singleStory=true)

お手本 storybook(http://shinonome.io/frontend-basic-todoapp-storybook/?path=/story/atoms-title--default)


まず、今回の課題の中で最も初歩的なコンポーネントである Atoms/Title を作成していきましょう。

以下のディレクトリ構造を参考に二つのファイルindex.jsxindex.stories.jsx(旧テンプレートの場合はstory.jsx)を作成してください。

2023年/9月にテンプレートリポジトリを更新した影響でファイル名の指定など一部変更点があるためご注意ください。

新旧テンプレートの見分け方

.storybookフォルダ内のmain.jsファイルにてstoriesをご確認ください。パスのファイル名が*.stories.jsxでしたら新テンプレート、story.jsxでしたら旧テンプレートです。

新テンプレート

module.exports = {
  stories: ["../src/**/*.stories.jsx"],
  //略
};

旧テンプレート

module.exports = {
  stories: ["../src/**/story.jsx"],
  //略
};

ディレクトリ構造

src
  └ components
    └ Atoms
      └ Title
        ├ index.jsx
        ├ story.jsx (旧テンプレートの場合)
        └ index.stories.jsx (新テンプレートの場合)

index.jsx の記述

index.jsxを開いて編集していきます。

最初に、コンポーネントのひな形となる以下のコードを記入しましょう。 コピペでも構いませんが、意味を理解してから次へ進むようにしてください。

import React from "react";

export const Title = () => {
  return null;
}

コードの説明をします。

まず、一行目では react パッケージを React という名前でインポートしています。 この行は今回使う React 等のバージョンでは必須の記述なので忘れず記入してください。 コードの中で React を直接使用していなくても書いてないとエラーが発生することがあります。

次に 3 行目~5 行目の記述で、Title コンポーネントを定義しています。 React のコンポーネントはこのように関数の形で表されます。(今回はアロー関数の形で書いています) また、古いコードだと class として定義しているものもあり、どちらでも動作します。 ですが、class の方は記述量が増え煩雑になるため、特段の理由が無ければ関数として定義することをおすすめします。 関数は返り値として、コンポーネントの中身を表す、JSX 形式で記述された DOM 要素を返します。 これは、この後で記述するので一旦 null としています。

3 行目のexport部分でTitle コンポーネントをこのファイルのエクスポートとして設定しています。 これによって、外部ファイルから、import (自由な名前 Titleとすると分かりやすい) from "(Atoms/Titleへのパス)";と記述することで この Title コンポーネントを呼び出して使えるようになります。

続いて、コンポーネントの中身を記述していきましょう。 先ほど述べた通り、コンポーネント内の要素は JSX という形式で記述します。 これは細かい違いは多々あるものの、基本的には HTML と同じ記法で書くことができます。 js ファイルの中に HTML を書くのは気持ちが悪いかもしれませんが、慣れて行きましょう。 実際にSIMPLE TODO APPのテキストを表示する JSX を先ほどの return の中に書いていきます。

const Title = () => {
  return <div>SIMPLE TODO APP</div>;
}

このようになります。今回は便宜上 div 要素を用いています。

index.stories.jsx (story.jsx)の記述

ここで、index.jsx の内容がどのように描画されるか確認していきましょう。 先ほど作成したindex.stories.jsxを開いて編集していきます。(旧テンプレートの人はstory.jsx)

次のコードを記入しましょう。

import { Title } from "./index";

export default { component: Title };

export const Default = {};

このファイルは先ほど作成したコンポーネントを storybook に登録するためのものです。

簡単にコードの解説を行います。

1行目import{ Title } from "./index";

これは、現在のディレクトリにある**index.jsx**ファイルからTitleをインポート(読み込み)しています。Title部分はReactのコンポーネントを指しています(export const Title =…)。

2行目export default { component: Title };

これは、Storybookにこのファイルがどのコンポーネントのストーリー(表示例)であるかを伝えるためのメタデータをエクスポートしています。ここではTitleがそのコンポーネントとして指定されています。

3行目export const Default = {};

これは、Defaultという名前のストーリーをエクスポートしています。このストーリーは、特に設定されていないので、デフォルトの状態でTitleが表示されることを意味します。ここを設定することで同じコンポーネントを複数のスタイル(色違い、大きさ違いなど)で表示させることができます。

詳しく知りたい方は公式ドキュメントをご確認ください。 コンポーネントを作成する際は毎回これをコピペしていただいて構いません。 ただし、1,2行目の title の中身はそのコンポーネントのカテゴリと名前を表すので、ファイルごとに変更するようにしましょう。 例えば今回は Title コンポーネントなので、Titleとしています。

storybook の起動

作成できたら storybook を起動して作成した Title コンポーネントをプレビューしましょう。 Terminal を開き、package.json の存在するディレクトリ(リポジトリの一番上のディレクトリ)にいることを確認して、 yarn sbを実行しましょう。 しばらく処理が走ったあと、エラーが無ければ http://localhost:6006 から storybook のページが閲覧できるようになります。 エラーが出た場合はできる限りエラーの内容を読み、今までの手順を間違えていないか確認しましょう。 解決が難しいと判断したら気軽に PGrit 等で講師に質問してください。

さて、storybook のページを開けたら、画面左のサイドバーから Atoms フォルダ内の Title コンポーネントを選択しましょう。(先ほど story.jsx の title に記述した内容に対応しています) すると、画面中央部に SIMPLE TODO APP と表示されると思います。(黒地に黒い文字なので見づらいですが) これが先ほど作成した Title.jsx コンポーネントとなります。

ここでする必要はありませんが、storybook を停止したい場合は実行中のターミナルでCommand(WindowsならCtrl)+Cを入力してください。

スタイルの適用

このままでは見た目が貧相なので、テキストに対しスタイルを指定しましょう。 ただし、今回は css ファイルを作成せず、React のプラグインである styled-components を利用します。 これは一言で表すと、スタイル付きの React コンポーネントを生成するツール群になります。 使ってみた方が理解は進むと思うので、早速書いて行きましょう。 index.tsxファイルの先頭に以下の import 文を追加してください。

import styled from "styled-components";

これで styled というエイリアスを用いて styled-components の機能を呼び出せるようになりました。 次に、これを使ってタイトルのテキストを表す StyledText コンポーネントを作成しましょう。 ファイルの一番下に以下の記述を追加してください。

const StyledText = styled.div`
  color: white;
`;

見慣れない記述ですが一旦解説は後回しにします。 次に、先ほど作った Title コンポーネントの return の中身を次のように書き換えてください。

return <StyledText>SIMPLE TODO APP</StyledText>;

先ほど div 要素だったものを作成した StyledText に置き換えました。 JSX の記述においてはこのように React コンポーネントと HTML 要素は同列に扱うことができます。 (HTML 要素と同じ名前の React コンポーネントが最初から定義されていると考えてもいい)

この状態で storybook のページを見てみましょう。先ほど停止してしまった場合は再びyarn sbで起動し、localhost:6006 を開いてください。

Atoms/Title コンポーネントのSIMPLE TODO APPの文字が白くなっているのが確認できると思います。

ここで、先ほど定義した StyledText コンポーネントを見てみましょう。

const StyledText = styled.div`
  color: white;
`;

styled.div に続いて、の中にcolor:white;の記述があります。 ここまで来た皆さんならすぐ分かると思いますが、これは css で文字色を白にする記述となります。 すなわち、こので囲われている部分に記されたスタイルが SIMPLE TODO APP に適用されているわけです。 試しにfont-size: 50px;を追加してみると、文字が大きくなります。

また、styled."div"と書かれているところを styled."a"に変更して、storybook のページに表示されている SIMPLE TODO APP の要素を検証ツールで 調べてみましょう。すると、a 要素になっていることが分かると思います。 すなわち、styled.の後ろに続く関数名によって、生成する DOM 要素を変更することもできます。(定義されているものに限る) 今回は div でいいので戻しておきましょう。

これが、styled-components によるスタイルの記述方法となります。 手順をまとめると、

  1. const 要素名 = styled.(最終的なDOM要素名)(スタイル);でコンポーネントを定義。
  2. メインのコンポーネントの return 内で<要素名>(中身)</要素>として使用。

となります。

定数の使用

今回の課題では、テンプレートリポジトリの中にスタイルで使用する色等の値を定数として既に定義してあります。

src/variables/ディレクトリの中に入っている js ファイルを開いて見ましょう。例えばcolor.jsの中では、 COLOR というオブジェクトを定義し、その中で BLACK、RED 等の色の名前をキーとして、"#232324"等のカラーコードが格納されています。 そして、外部から参照できるように COLOR オブジェクトをインポートしています。 使いまわす値はこのようになるべく一つのファイルで管理した方が後で変更が楽です。

これと同様に、breakpoint.jsfont_family.jstexts.jsも定義されています。 課題の中でスタイルを記述する際に、色、フォントの種類、テキストサイズ、ブレークポイントには必ずこれらで定義された 変数を使うようにしてください。

以下に色とテキストの定数をスタイルの中で使用する例を示します。

import COLOR from "../../../variables/color"; // color.jsのCOLOR定数をインポート fromの中身はvariables/color.jsへの相対パス
import TEXT from "../../../variables/text"; // text.jsのTEXT定数をインポート

...

/* styled-componentの定義 文字色にGREEN、背景色にLIGHT_BLACK、テキストのサイズはMを指定 */
const StyledExample = styled.div`
  color: ${COLOR.GREEN};
  background: ${COLOR.LIGHT_BLACK};
  ${TEXT.M}
`;

COLOR、FONTFAMILY、BREAKPOINT は全て同じようにcssプロパティ: ${定数.キー};の形で使えますが、 TEXT は css プロパティの指定まで含んでいるため、いきなり${TEXT.サイズ}の形で書くことに注意してください。

Atoms/Title を完成させる

先ほど作成したsrc/components/Atoms/Title/index.jsxの中に定義した StyledText の文字色を、定数の COLOR.WHITE にしましょう。 加えて、figma の指定をよく見ながら、

storybook 上で figma のお手本と同じ見た目になったことを確認したら、 プルリクエストを出し、レビューを受けてください。

レビューの依頼は、HTML/CSS課題と同様に、discordの#fe-課題レビュー チャンネルへ

を投稿してください。

<aside> ⚠️

要対応

おそらく、CI(PRを作成した時に下に出てくるチェック)が❌マークを出して失敗するはずです。

chromatic.yml, gh-pages.yml について、以下の二行を探して右のように更新すると直ります!

runs-on: ubuntu-20.04 → ubuntu-22.04
uses: actions/cache@v2 → actions/cache@v4

課題を作った当初からそれぞれバージョン更新が行われていて、古いバージョンが使えなくなっているという話です。

</aside>