今度はVite + Shadcn (+ Vitest)で冷蔵庫内管理アプリをつくった
前回、Vite + Jest + Testing Libraryの環境を作りましたが、今回はVitestを使って環境作ってAIにコード書いてもらうことにします。
準備
前回やってるViteとかPrettierとかは割愛します。
VitestとTesting Libraryをインストール
npm install -D vitest @testing-library/react @testing-library/dom @types/react @types/react-dom jsdom @testing-library/jest-dom
インストールしたら、package.jsonのスクリプトにtest
を追加します。
{
"name": "reizouko-manager",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"test": "vitest"
},
...
}
vite.config.ts
を以下に書き換え
- import { defineConfig } from 'vite'
+ import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react-swc'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ setupFiles: './vitest.setup.ts',
+ },
})
vitest.setup.ts
にインポート入れておきます。
import '@testing-library/jest-dom'
tsonfig.app.json
の追記はこう。
{
"compilerOptions": {
...
+ "esModuleInterop": true,
+ "types": ["vitest/globals", "@testing-library/jest-dom"]
},
"include": ["src"]
}
あとはテストファイルを作って動作確認して完了!
本題:冷蔵庫内の賞味期限を管理したい!
賞味期限ギリギリまで残してしまったり、見たら賞味期限切れてた………ということが結構あるので、買ったものの賞味期限を管理したいなーとずっと思っていました。
前に同じものをLovableで作ったのですが、コード読む気力がなくてそのままにしてたので、一度作り直すことにしました。
shadcnをインストール
UIライブラリはshadcnでやってみます。結構スキ。
viteはすでに導入済みなので、その次のコマンドからやっていきます。
npm install tailwindcss @tailwindcss/vite
src/index.css
の中身を、Tailwindに置き換えます。
@import "tailwindcss";
tsconfig.json
とtsconfig.app.json
にbaseUrl
とpaths
を追記。
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
}
{
"compilerOptions": {
...
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"]
}
次に、アプリがエラーなしでパスを解決できるように@types/node
をインストールして、vite.config.ts
を編集します。
npm install -D @types/node
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react-swc";
+ import path from "path";
+ import tailwindcss from "@tailwindcss/vite";
// https://vite.dev/config/
export default defineConfig({
- plugins: [react()],
+ plugins: [react(), tailwindcss()],
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ },
},
test: {
globals: true,
environment: "jsdom",
setupFiles: "./vitest.setup.ts",
},
});
CLIを実行してセットアップします。
npx shadcn@latest init
多分このベースカラーのslate
gray
zinc
neutral
stone
が該当するっぽい。
なんでもいいけどとりあえずneutral
にします。
選択すると
It looks like you are using React 19.
Some packages may fail to install due to peer dependency issues in npm (see https://ui.shadcn.com/react-19).
黄色い文字で出てくるのですが、Use --force
でインストールします。
Lint対象からshadcn関連を無視するように追記していきます。
public
*.d.ts
+ src/lib/**/*
+ src/components/ui/**/*
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
import prettier from "eslint-config-prettier";
export default tseslint.config(
- { ignores: ["dist"] },
+ { ignores: ["dist", "src/lib/**/*", "src/components/ui/**/*"] },
...
);
次に、テストからも除外するためvite.config.ts
も書き換えます。
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react-swc";
import path from "path";
import tailwindcss from "@tailwindcss/vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
test: {
globals: true,
environment: "jsdom",
setupFiles: "./vitest.setup.ts",
+ exclude: ["src/lib/**/*", "src/components/ui/**/*"],
+ passWithNoTests: true,
},
});
passWithNoTests
はテストなくてもエラーにならないようにしました。現状何のテストもないけど、shadcnは入れてるから「テストない!」ってエラーが出るんですよね。
TODOリストを作る
実装は基本Cursor(claude-3.7-sonnet)でやってもらうので、あいまいな仕様書ではなくがっちり作ります。
一旦MVPとして実装するため、こんな感じでまとめました。
- コンポーネントはshadcnを使用して実装すること
- レスポンシブでどのデバイスにも適切に表示されるようにすること
- 食材のリストはローカルストレージで管理する
- ドロワーで食材登録フォームが開く
- [ ] 食品名の入力欄
- [ ] 食品の賞味期限の入力欄
- [ ] デフォルトでは5日間とする
- [ ] 登録ボタン
- [ ] 登録をキャンセルするボタン
- 食材リスト
- [ ] フォームで登録した食材が1つずつカード型のレイアウトで表示される
- [ ] 食品名(例:きゅうり、たまご)
- [ ] 賞味期限までの日数を、賞味期限から自動計算する(例:あと3日)
- [ ] カードに、賞味期限までの日数によって背景色をつける
- 5日以上ある:緑
- 5日〜3日:黄色
- 3日〜当日:赤
- 当日を過ぎた:黒
- [ ] 削除ボタン
もちろんChatGPTによる添削つきです。日数の自動計算の明記とかが抜けてたのでありがたい。
あと、テストも併せて実装してもらうので
- 実装する各機能には、可能な限りテストも含めて実装すること
- 使用ライブラリ:Vitest + @testing-library/react
これでAuto-runで実装してもらいます。
なお、あとから気づきましたがテストはクエリの優先度も明記しておいたほうがいいです。
最終的に
# アプリについて
- コンポーネントはshadcnを使用して実装すること
- レスポンシブでどのデバイスにも適切に表示されるようにすること
- 食材のリストはローカルストレージで管理する
- ドロワーで食材登録フォームが開く
- [ ] 食品名の入力欄
- [ ] 食品の賞味期限の入力欄
- [ ] デフォルトでは5日間とする
- [ ] 登録ボタン
- [ ] 登録をキャンセルするボタン
- 食材リスト
- [ ] フォームで登録した食材が1つずつカード型のレイアウトで表示される
- [ ] 食品名(例:きゅうり、たまご)
- [ ] 賞味期限までの日数を、賞味期限から自動計算する(例:あと3日)
- [ ] カードに、賞味期限までの日数によって背景色をつける
- 5日以上ある:緑
- 5日〜3日:黄色
- 3日〜当日:赤
- 当日を過ぎた:黒
- [ ] 削除ボタン
- [ ]
# テストについて
- 実装する各機能には、可能な限りテストも含めて実装すること
- 使用ライブラリ:Vitest + @testing-library/react
- テストは主に振る舞い(behavior)に着目し、ユーザー視点で書くこと
- クエリの使用優先度([Testing Libraryのガイドライン](https://testing-library.com/docs/queries/about/)に準拠):
### 誰でもアクセスできるクエリ(最優先)
1. `getByRole`
2. `getByLabelText`
3. `getByPlaceholderText`
4. `getByText`
5. `getByDisplayValue`
### セマンティッククエリ
1. `getByAltText`
2. `getByTitle`
### テスト専用クエリ(最終手段)
1. `getByTestId`
- テストは可読性を意識し、3A(Arrange → Act → Assert)の流れで記述してください
- それぞれのフェーズが分かるよう、空行やコメントで明確に区切るとより良いです
ざっくりテストみた時に「これは足したほうがいいかな〜」って思った最低限を足したので、コードレビュー後にまた追記するかも。
コードレビューについては後日。
GitHub Pagesに公開する
GitHub Actions等についてはViteに完璧にまとまってるのでそちらを参照します。
が、今回インストールしたバージョンのreact-day-picker
がReact19に対応してなくて色々作業したのでその備忘録を。
まずはパッケージを全部最新にする
インストールされているバージョンが古いのでは?と思ったので、ncu
コマンドで調べてみました。
@eslint/js ^9.21.0 → ^9.24.0
@vitejs/plugin-react-swc ^3.8.0 → ^3.8.1
date-fns ^3.6.0 → ^4.1.0
eslint ^9.21.0 → ^9.24.0
eslint-plugin-react-hooks ^5.1.0 → ^5.2.0
globals ^15.15.0 → ^16.0.0
react ^19.0.0 → ^19.1.0
react-day-picker ^8.10.1 → ^9.6.5
react-dom ^19.0.0 → ^19.1.0
typescript ~5.7.2 → ~5.8.3
typescript-eslint ^8.24.1 → ^8.29.1
vite ^6.2.0 → ^6.2.6
これでただ更新しただけでうまくいけば「は〜よかったよかった〜」で済みますが、残念ながらそうはならず。
どうもCalendarコンポーネントのIconLeft
とIconRight
がなくなった模様。
こう言う時は大元のドキュメントを参照します。
やっぱり手段はあるみたい。
The current shadcn/ui does not show the latest version. For a proper integration, please refer to date-picker.luca-felix.com.
と言うわけで、ここで書かれているCalendar.tsx
をこのまま使わせていただきます。
これでnpm run build
ができたので、今度こそGitHub Actionsが通るはず!
完成!ここまでで大体4時間くらいでしょうか。
あとはじっくりコードレビューと改善を繰り返して行こうと思います。(コードレビューはスクラップにしてから記事にまとめることになりそうな気がする)
Discussion