diff --git a/packages/spindle-ui/src/Form/TextField/TextField.mdx b/packages/spindle-ui/src/Form/TextField/TextField.mdx index f4a07172a..6d27f4851 100644 --- a/packages/spindle-ui/src/Form/TextField/TextField.mdx +++ b/packages/spindle-ui/src/Form/TextField/TextField.mdx @@ -98,3 +98,60 @@ import '@openameba/spindle-ui/Form/TextField.css'; - `:user-invalid` (Baseline 2023) - [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-invalid) - フォームバリデーション時にエラー表示に使用しています +## Medium With Value + +入力値がある状態です。 + + + + + `} +/> + + + `} +/> + +## Medium Disabled + +無効化状態です。 + + + + + `} +/> + + + `} +/> + +## Medium With Long Text + +長いテキストを含む状態です。 + + + + + `} +/> + + + `} +/> + diff --git a/packages/spindle-ui/src/Form/TextField/TextField.stories.tsx b/packages/spindle-ui/src/Form/TextField/TextField.stories.tsx index cf3133df8..43a7406d7 100644 --- a/packages/spindle-ui/src/Form/TextField/TextField.stories.tsx +++ b/packages/spindle-ui/src/Form/TextField/TextField.stories.tsx @@ -62,3 +62,37 @@ export const MediumWithError: Story = { /> ), }; + +export const MediumWithValue: Story = { + render: (args) => ( + + ), +}; + +export const MediumDisabled: Story = { + render: (args) => ( + + ), +}; + +export const MediumWithLongText: Story = { + render: (args) => ( + + ), +}; diff --git a/packages/spindle-ui/src/Form/TextField/TextField.test.tsx b/packages/spindle-ui/src/Form/TextField/TextField.test.tsx index a18e9f47a..45bf434f6 100644 --- a/packages/spindle-ui/src/Form/TextField/TextField.test.tsx +++ b/packages/spindle-ui/src/Form/TextField/TextField.test.tsx @@ -21,4 +21,54 @@ describe('', () => { expect(screen.getByRole('textbox')).toEqual(ref.current); }); + + test('renders with id attribute', () => { + render(); + + expect(screen.getByRole('textbox')).toHaveAttribute('id', 'username'); + }); + + test('renders with placeholder', () => { + render(); + + expect(screen.getByPlaceholderText('Enter text')).toBeInTheDocument(); + }); + + test('renders with disabled attribute', () => { + render(); + + expect(screen.getByRole('textbox')).toBeDisabled(); + }); + + test('renders with type attribute', () => { + render(); + + expect(screen.getByRole('textbox')).toHaveAttribute('type', 'email'); + }); + + describe('Error State Accessibility', () => { + test('renders with aria-invalid attribute', () => { + render(); + + expect(screen.getByRole('textbox')).toHaveAttribute('aria-invalid'); + }); + + test('renders with aria-errormessage attribute', () => { + render(); + + expect(screen.getByRole('textbox')).toHaveAttribute( + 'aria-errormessage', + 'text-error', + ); + }); + + test('renders with aria-describedby attribute', () => { + render(); + + expect(screen.getByRole('textbox')).toHaveAttribute( + 'aria-describedby', + 'text-error', + ); + }); + }); }); diff --git a/packages/spindle-ui/src/Form/TextField/design-doc.md b/packages/spindle-ui/src/Form/TextField/design-doc.md new file mode 100644 index 000000000..ad30f9fd6 --- /dev/null +++ b/packages/spindle-ui/src/Form/TextField/design-doc.md @@ -0,0 +1,161 @@ +# TextField + +## 概要・背景 + +TextFieldは、1行のテキスト入力を提供するフォームコンポーネントです。標準のHTML input要素をラップし、Spindleのデザインシステムに沿ったスタイルを提供します。ユーザー名、メールアドレス、検索キーワードなど、短いテキストの入力に使用されることを想定しています。 + +複数行のテキスト入力が必要な場合は、[Textarea](https://ameba-spindle.web.app/?path=/story/form-textarea--text-area)コンポーネントを使用してください。 + +## 使用例 + +### DO + +TextFieldは、`id`プロパティを必須として使用します。[InputLabel](https://ameba-spindle.web.app/?path=/story/form-inputlabel--input-label)と組み合わせて、入力欄に明確なラベルを提供してください。 + +```tsx +ユーザー名 + +``` + +#### エラー状態の表示 + +入力値が不正な場合は、`hasError`プロパティを指定してエラー状態を表示します。[InvalidMessage](https://ameba-spindle.web.app/?path=/story/form-invalidmessage--invalid-message)と組み合わせることで、ユーザーに具体的なエラー内容を伝えることができます。 + +エラーメッセージとの紐付けには、`aria-invalid`と`aria-errormessage`(または`aria-describedby`)を使用します。 + +```tsx +メールアドレス + + + メールアドレスの形式が正しくありません + +``` + +#### サイズのバリエーション + +`variant`プロパティで、`large`(48px)または`medium`(40px)のサイズを選択できます。デフォルトは`medium`です。 + +```tsx + +``` + +### DO NOT + +このコンポーネントは`className`プロパティを受け取らないため、スタイルを直接変更できません。レイアウトやスタイルを調整する必要がある場合は、このコンポーネントを別のコンポーネントでラップするなど、コンポーネントを組み合わせて実装してください。 + +## 要素 + +### Design Tokens + +- Surface Primary (背景色) +- Border Medium Emphasis (ボーダー色) +- Border High Emphasis (フォーカス時のボーダー色) +- Text High Emphasis (入力テキスト色) +- Text Disabled (プレースホルダーテキスト色) +- Focus Ambiguous (フォーカス時のシャドウ色) +- Surface Caution Light (エラー時の背景色) +- Border Caution (エラー時のボーダー色) + +### プロパティ + +```ts +type Props = { + // フォーカス制御のためのref + ref?: React.Ref; + // エラー状態の表示 + hasError?: boolean; + // 入力欄のID (必須) + id: string; + // サイズのバリエーション (デフォルト: medium) + variant?: 'large' | 'medium'; + // 標準のinput要素の属性 (className以外) +} & Omit, 'className'>; +``` + +## 実装例 + +### React実装の例 + +```tsx + +``` + +### 書き出されるマークアップ + +```html + +``` + +エラー状態の場合は`is-error`クラスが付与されます。 + +```html + +``` + +## アクセシビリティ + +- [情報や関係性を明確にする](https://a11y-guidelines.ameba.design/1/3/1/)[基本必須] + - [ ] InputLabel要素のfor属性で入力欄と関連付けている + - [ ] idプロパティが適切に設定され、対応するラベルと一致している +- [テキストや文字画像のコントラストを確保する](https://a11y-guidelines.ameba.design/1/4/3/)[基本必須] + - [ ] SpindleカラーパレットのTheme Colorsを適切に使い分けている +- [テキストサイズを拡大縮小できる](https://a11y-guidelines.ameba.design/1/4/4/)[基本必須] + - [ ] 画面を200%拡大・文字サイズを2倍に変更しても、適切に文字が折り返され、情報が欠落していない +- [キーボード、タッチデバイスで操作できる](https://a11y-guidelines.ameba.design/2/1/1/)[基本必須] + - [ ] Tabキーでフォーカスでき、キーボードで文字入力ができる +- [フォーカスを見えるようにする](https://a11y-guidelines.ameba.design/2/4/7/)[基本必須] + - [ ] 入力欄は、フォーカスの状態が見える(`:focus-visible`擬似クラスで適切なスタイルが適用されている) +- [ターゲットのサイズを理解する](https://a11y-guidelines.ameba.design/2/5/5/)[できれば] + - [ ] タップ領域は最小でも40px x 40px以上確保している +- [入力を補助する](https://a11y-guidelines.ameba.design/3/3/2/)[できれば] + - [ ] 適切な`type`属性を指定している + - [ ] 適切な`autocomplete`属性を指定している +- [エラーを特定できる](https://a11y-guidelines.ameba.design/3/3/1/)[基本必須] + - [ ] エラー時に`aria-invalid="true"`を指定している + - [ ] エラーメッセージを`aria-errormessage`または`aria-describedby`で関連付けている +- [HTMLを正しく記述する](https://a11y-guidelines.ameba.design/4/1/1/)[基本必須] + - [ ] HTML仕様に準拠した実装をしている + - [ ] セマンティックな``要素を使用している + +## テスト方針 + +### ユニットテスト (Testing Library) + +- TextFieldがレンダリングされること +- キーボードで文字入力ができること +- `ref`が適切に転送されること +- 標準のHTML属性(`placeholder`、`disabled`、`type`など)が適切に反映されること +- エラー状態時のアクセシビリティ属性(`aria-invalid`、`aria-errormessage`、`aria-describedby`)が適切に反映されること + +### ヴィジュアルリグレッションテスト (Storybook) + +- 通常状態(large/medium) +- エラー状態(large/medium) +- プレースホルダー表示 +- 入力値がある状態 +- 無効化状態(`disabled`) +- 長いテキストを含む状態 + +## リンク集 + +- [HTMLのinput要素 (MDN)](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input) +- [HTMLのinput要素のtype属性 (MDN)](https://developer.mozilla.org/ja/docs/Web/HTML/Element/input#input_types)