ハコヅミを作る #13:ギアのおすすめ度を、★をタップして付ける

前回(#12)のギアの画像に続いて、今回はおすすめ度です。

使ってみてよかったギア、いまひとつだったギアを、あとから見返せるようにしたいと思っていました。ギアごとに、おすすめ度を5段階で付け、その理由も書き残せるようにします。 Notion の構想では「ギアのおすすめ度」として挙げていたもので、将来、おすすめ情報を他の人と共有する(ステップ2)ときの土台にもなります。


見た目

ギアの一覧では、おすすめ度が付いているギアの製品名の後ろに、★が出ます。

ギアの一覧。いくつかのギアの製品名の後ろに、オレンジ色の ★★★★★ や ★★★☆☆ が表示されている

ギアの一覧(上のほうを切り出したもの)。おすすめ度はオレンジの★で出る

画面は、この記事の時点のコードを手元で動かして撮ったものです。データは#7でNotionから取り込んだ、実際のギアを入れています(おすすめ度は、記事用に付けたものです)。

編集画面では、★をタップして選びます。3つ目の★をタップすると ★★★☆☆ になります。

おすすめ度の欄。5つの★がすべてオレンジで塗られ、右に「未設定に戻す」ボタン。その下におすすめ理由の入力欄

編集画面のおすすめ度とおすすめ理由


Issue で決めたこと

最初の下書きは「おすすめ度を 1〜5 から選ぶ(ラジオボタン)」だけでした。下書きを見て、2点を足しました。

  • おすすめ理由(任意)も入れられるようにする:Notion の構想にも「おすすめ度やおすすめ理由」とあった。数字だけだと、なぜそう付けたかを忘れる
  • 入力は、★をタップする見た目にする:ラジオボタンが5つ並ぶより、おすすめ度らしい

テーブルは、gears に列を2つ足すだけです。

列 内容
rating おすすめ度。1〜5 の整数、null なら未設定
rating_note おすすめ理由。1000 文字まで

見た目は★、中身はラジオボタン

★をタップする見た目は、JavaScript で★を並べて、クリックで値を変える形でも作れます。 ただそれだと、次のことを自分で作り込む必要があります。

  • JavaScript が動かなくても、フォームで値が送られること
  • キーボード(矢印キー)で選べること
  • 読み上げで「おすすめ度 3」のように読まれること

そこで、中身はふつうのラジオボタンにして、見た目だけを★にする形にしました。ラジオボタンなら、この3つはブラウザが最初からやってくれます。

{#each levels as level (level)}
	<label class="cursor-pointer">
		<!-- ラジオボタンは見えないようにする(sr-only)。読み上げやキーボードでは使える -->
		<input
			type="radio"
			name="rating"
			value={level}
			checked={selected === level}
			aria-label={`おすすめ度 ${level}`}
			class="peer sr-only"
			onchange={() => (selected = level)}
		/>
		<!-- 選んだ★までを塗る。キーボードで選んでいるときは枠を出す -->
		<span
			aria-hidden="true"
			class={[
				'text-3xl peer-focus-visible:outline-2',
				level <= selected ? 'text-accent' : 'text-line'
			]}>{level <= selected ? '★' : '☆'}</span
		>
	</label>
{/each}

ポイントは Tailwind の peer です。見えないラジオボタンにキーボードの焦点があるとき、隣の★に枠を出せます。見た目を★にしても、「今どこを選んでいるか」がキーボードでも分かります。

「未設定」に戻すには

ラジオボタンには弱点が1つあります。一度どれかを選ぶと、どれも選んでいない状態に戻せないことです。 そこで、選んでいるときだけ「未設定に戻す」ボタンを出しました。押すと、どのラジオボタンにもチェックが付いていない状態になり、フォームでは値が送られず、サーバーで null になります。

差し色を初めて使った

#11で、配色「森」の差し色として焚き火のオレンジ(accent)を用意しましたが、使いどころが決まらず、どこにも使っていませんでした。 ★の色にちょうどよかったので、今回が初めての出番です。緑ばかりの画面の中で、おすすめ度だけが目に入るようになりました。


DB の制約は付けなかった

おすすめ度は 1〜5 の整数だけにしたい値です。DB の制約(CHECK)で守る方法もありますが、今回は付けていません。

ハコヅミではマイグレーションを Drizzle で作っていて、既存のテーブルに CHECK 制約を足すと、テーブルを作り直すマイグレーションが作られます。#5で外部キーを足したときに、データを移しながら作り直す大変さを経験しました。 列を足すだけなら、マイグレーションは ALTER TABLE ... ADD の2行で済みます。範囲はアプリの入力チェックで守り、そのチェックをユニットテストで確かめる形にしました。

if (values.rating !== '' && !/^[1-5]$/.test(values.rating)) {
	errors.rating = 'おすすめ度を選び直してください';
}

画面を通さずに rating=6 や rating=3.5 を送っても保存されないことは、E2E でも確かめています。


受入確認で、AC を自動テストに回した

受入確認は、ほぼそのまま OK でした。1つだけ、ACを「(自動テストで確認)」に書き直しています。

おすすめ理由が 1000 文字を超えると保存されず、エラーが表示される

受入確認のために 1000 文字を超える文章を用意して貼るのは、手間のわりに得るものが少ないためです。E2E で 1001 文字だとエラーになること、ユニットテストで 1000 文字ちょうどは通ることを確かめているので、受入はそのテストの結果で行いました。

#4で決めた「画面から確かめられないACは、自動テストで確認とする」の使い方が、少し広がりました。画面から確かめられないものだけでなく、画面から確かめるのが割に合わないものも、自動テストに任せてよいと考えています。前回(#12)の「1 MB を超える画像」も同じです。


おわりに

  • おすすめ度は、見た目は★、中身はラジオボタン。キーボードや読み上げはブラウザに任せる
  • 「未設定に戻す」は、ラジオボタンの弱点を補うボタン
  • 範囲は DB の制約ではなく入力チェックで守る(テーブルの作り直しを避ける)
  • 受入確認で試すのが割に合わない AC は、自動テストに回す

画像とおすすめ度がそろって、ギアの一覧が少し「自分のギアの記録」らしくなってきました。 この2つとプランの改善は、あわせて本番にもリリースしています。

← トップページに戻る