Formコンポーネント
デザインシステムに準拠した統一的なフォームコンポーネント群のデモとテスト
Input
テキスト入力コンポーネント。様々なタイプとアイコンに対応
Input Example
TSX
import { Input, FormLabel, FormGroup } from "@/components/Form";
// 基本的な使用例
<div>
<FormLabel htmlFor="text" size="md">テキスト入力</FormLabel>
<FormGroup>
<Input
id="text"
type="text"
size="md"
placeholder="テキストを入力..."
value={text}
onChange={(e) => setText(e.target.value)}
/>
</FormGroup>
</div>
// アイコン付き
<div>
<FormLabel htmlFor="search" size="md">検索</FormLabel>
<FormGroup>
<Input
id="search"
type="search"
size="md"
leftIcon="search"
placeholder="検索..."
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</FormGroup>
</div>
// エラー表示
<div>
<FormLabel htmlFor="email" size="md" required>メールアドレス</FormLabel>
<FormGroup error={error}>
<Input
id="email"
type="email"
size="md"
validation={error ? "error" : "none"}
placeholder="email@example.com"
value={email}
onChange={handleEmailChange}
/>
</FormGroup>
</div>Inputのデモ
8文字以上で入力してください
サイズバリエーション
Textarea
複数行テキスト入力コンポーネント。自動リサイズ機能付き
Textarea Example
TSX
import { Textarea, FormLabel, FormGroup } from "@/components/Form";
// 基本的な使用例
<div>
<FormLabel htmlFor="message" size="md">メッセージ</FormLabel>
<FormGroup>
<Textarea
id="message"
size="md"
placeholder="メッセージを入力..."
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</FormGroup>
</div>
// 自動リサイズ
<div>
<FormLabel htmlFor="description" size="md">説明</FormLabel>
<FormGroup help="自動でリサイズされます(最大10行)">
<Textarea
id="description"
size="md"
autoResize
minRows={3}
maxRows={10}
placeholder="説明を入力..."
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</FormGroup>
</div>
// リサイズ制御
<div>
<FormLabel htmlFor="comment" size="md">コメント</FormLabel>
<FormGroup>
<Textarea
id="comment"
size="md"
resize="vertical"
placeholder="コメントを入力..."
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
</FormGroup>
</div>Textareaのデモ
入力内容に応じて自動的に高さが変わります(最大10行)
右下のハンドルで縦方向にリサイズできます
サイズバリエーション
リサイズタイプ
リサイズ不可(none)
縦方向のみ(vertical)
横方向のみ(horizontal)
両方向(both)
Select
セレクトボックスコンポーネント
Select Example
TSX
import { Select, FormLabel, FormGroup } from "@/components/Form";
// 基本的な使用例
<div>
<FormLabel htmlFor="country" size="md">国を選択</FormLabel>
<FormGroup>
<Select
id="country"
size="md"
placeholder="選択してください"
value={country}
onChange={(e) => setCountry(e.target.value)}
options={[
{ value: "jp", label: "日本" },
{ value: "us", label: "アメリカ" },
{ value: "uk", label: "イギリス" },
{ value: "fr", label: "フランス" },
]}
/>
</FormGroup>
</div>
// グループ化
<div>
<FormLabel htmlFor="category" size="md">カテゴリを選択</FormLabel>
<FormGroup>
<Select
id="category"
size="md"
placeholder="選択してください"
value={category}
onChange={(e) => setCategory(e.target.value)}
optionGroups={[
{
label: "プログラミング",
options: [
{ value: "js", label: "JavaScript" },
{ value: "ts", label: "TypeScript" },
{ value: "python", label: "Python" },
],
},
{
label: "デザイン",
options: [
{ value: "figma", label: "Figma" },
{ value: "sketch", label: "Sketch" },
{ value: "xd", label: "Adobe XD" },
],
},
]}
/>
</FormGroup>
</div>Selectのデモ
グループ化されたオプション
サイズバリエーション
Checkbox
チェックボックスコンポーネント(単一・グループ・indeterminate状態)
Checkbox Example
TSX
import { Checkbox, CheckboxGroup } from "@/components/Form";
// 単一Checkbox
<Checkbox
id="terms"
label="利用規約に同意する"
checked={agreed}
onChange={(e) => setAgreed(e.target.checked)}
/>
// デフォルトでチェック済み
<Checkbox
id="newsletter"
label="ニュースレターを受け取る"
checked={newsletter}
onChange={(e) => setNewsletter(e.target.checked)}
/>
// indeterminate状態(一部選択)
<Checkbox
id="select-all"
label="すべて選択"
indeterminate={isSomeFruitsSelected}
checked={isAllFruitsSelected}
onChange={(e) => handleSelectAllFruits(e.target.checked)}
/>
// CheckboxGroup - 縦レイアウト
<CheckboxGroup
options={[
{ value: "apple", label: "りんご" },
{ value: "banana", label: "バナナ" },
{ value: "orange", label: "オレンジ" },
]}
value={fruits}
onChange={setFruits}
direction="vertical"
/>
// CheckboxGroup - 横レイアウト
<CheckboxGroup
options={[
{ value: "email", label: "メール通知" },
{ value: "sms", label: "SMS通知" },
{ value: "push", label: "プッシュ通知" },
]}
value={notifications}
onChange={setNotifications}
direction="horizontal"
/>Checkboxのデモ
利用規約: 未同意
ニュースレター: 受け取る
選択中の果物: apple
通知設定: email, push
Radio
ラジオボタンコンポーネント(単一・グループ・横/縦レイアウト)
Radio Example
TSX
import { Radio, RadioGroup } from "@/components/Form";
// 単一Radio
<Radio
id="plan-free"
name="plan"
value="free"
label="無料プラン"
checked={plan === "free"}
onChange={(e) => setPlan(e.target.value)}
/>
<Radio
id="plan-pro"
name="plan"
value="pro"
label="プロプラン"
checked={plan === "pro"}
onChange={(e) => setPlan(e.target.value)}
/>
// RadioGroup - 縦レイアウト
<RadioGroup
name="size"
options={[
{ value: "small", label: "小" },
{ value: "medium", label: "中" },
{ value: "large", label: "大" },
]}
value={size}
onChange={setSize}
direction="vertical"
/>
// RadioGroup - 横レイアウト
<RadioGroup
name="color"
options={[
{ value: "red", label: "赤" },
{ value: "blue", label: "青" },
{ value: "green", label: "緑" },
]}
value={color}
onChange={setColor}
direction="horizontal"
/>Radioのデモ
Small
Medium(デフォルト)
Large
選択中のプラン: free
選択中のサイズ: medium
選択中の色: blue
RangeInput
レンジスライダーコンポーネント
RangeInput Example
TSX
import { RangeInput, FormLabel, FormGroup } from "@/components/Form";
// 基本的な使用例
<div>
<FormLabel htmlFor="volume" size="md">
音量: {volume}
</FormLabel>
<FormGroup>
<RangeInput
id="volume"
min="0"
max="100"
step="1"
value={volume}
onChange={(e) => setVolume(parseInt(e.target.value))}
/>
</FormGroup>
</div>
// 温度設定(小数点対応)
<div>
<FormLabel htmlFor="temperature" size="md">
温度: {temperature}°C
</FormLabel>
<FormGroup>
<RangeInput
id="temperature"
min="10"
max="30"
step="0.5"
value={temperature}
onChange={(e) => setTemperature(parseFloat(e.target.value))}
/>
</FormGroup>
</div>RangeInputのデモ
0〜100で調整
0.5°C刻みで調整
視覚的なフィードバック
明るさ: 75%
Complete Form Example
すべてのフォームコンポーネントを使った完全なフォーム例
Complete Form
TSX
import {
Input,
Textarea,
Select,
CheckboxGroup,
RadioGroup,
FormLabel,
FormGroup,
Checkbox,
} from "@/components/Form";
import { FormButton } from "@/components/Button";
function ContactForm() {
const [formData, setFormData] = useState({
name: "",
email: "",
category: "",
plan: "free",
features: [],
message: "",
agreed: false,
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// バリデーション & 送信処理
};
return (
<form onSubmit={handleSubmit} className="space-y-6">
{/* 名前 */}
<div>
<FormLabel htmlFor="name" size="md" required>
お名前
</FormLabel>
<FormGroup error={errors.name}>
<Input
id="name"
type="text"
size="md"
validation={errors.name ? "error" : "none"}
value={formData.name}
onChange={(e) => setFormData({...formData, name: e.target.value})}
/>
</FormGroup>
</div>
{/* メール */}
<div>
<FormLabel htmlFor="email" size="md" required>
メールアドレス
</FormLabel>
<FormGroup error={errors.email}>
<Input
id="email"
type="email"
size="md"
leftIcon="mail"
validation={errors.email ? "error" : "none"}
value={formData.email}
onChange={(e) => setFormData({...formData, email: e.target.value})}
/>
</FormGroup>
</div>
{/* カテゴリ */}
<div>
<FormLabel htmlFor="category" size="md" required>
カテゴリ
</FormLabel>
<FormGroup error={errors.category}>
<Select
id="category"
size="md"
placeholder="選択してください"
validation={errors.category ? "error" : "none"}
value={formData.category}
onChange={(e) => setFormData({...formData, category: e.target.value})}
options={[...]}
/>
</FormGroup>
</div>
{/* プラン */}
<div>
<FormLabel htmlFor="plan" size="md">
プラン選択
</FormLabel>
<RadioGroup
name="plan"
options={[...]}
value={formData.plan}
onChange={(value) => setFormData({...formData, plan: value})}
/>
</div>
{/* 機能 */}
<div>
<FormLabel htmlFor="features" size="md">
必要な機能
</FormLabel>
<CheckboxGroup
options={[...]}
value={formData.features}
onChange={(value) => setFormData({...formData, features: value})}
/>
</div>
{/* メッセージ */}
<div>
<FormLabel htmlFor="message" size="md" required>
メッセージ
</FormLabel>
<FormGroup error={errors.message}>
<Textarea
id="message"
size="md"
autoResize
validation={errors.message ? "error" : "none"}
value={formData.message}
onChange={(e) => setFormData({...formData, message: e.target.value})}
/>
</FormGroup>
</div>
{/* 同意 */}
<FormGroup error={errors.agreed}>
<Checkbox
id="agreed"
label="利用規約に同意する"
checked={formData.agreed}
onChange={(e) => setFormData({...formData, agreed: e.target.checked})}
/>
</FormGroup>
{/* 送信ボタン */}
<div className="flex gap-3">
<FormButton
type="submit"
variant="primary"
size="md"
loading={isSubmitting}
disabled={!formData.agreed}
>
送信
</FormButton>
<FormButton
type="button"
variant="secondary"
size="md"
onClick={handleReset}
>
リセット
</FormButton>
</div>
</form>
);
}