# FormGroup

> Wrapper cho field input — kèm label + helper text + error message. Standardize spacing và a11y giữa các form trong app.

## API

| Prop | Type | Default | Description |
|---|---|---|---|
| `label` | `ReactNode` | — | Label phía trên input |
| `helper` | `ReactNode` | — | Text mô tả/hướng dẫn (hiển thị khi không có error) |
| `error` | `ReactNode` | — | Error message — khi có, helper bị ẩn, hiển thị icon cảnh báo |
| `htmlFor` | `string` | — | Liên kết label → input qua `id` |
| `required` | `boolean` | `false` | Thêm dấu `*` đỏ sau label |
| `children` | `ReactNode` | required | Input/Select/Textarea bên trong |
| `className` | `string` | — | Override container |

## Variants

Không có variant rõ ràng — chỉ toggle giữa 3 state hiển thị (default · helper · error).

## Composition

```
<FormGroup label="…" htmlFor="email" required helper="…">
  <Input id="email" type="email" />
</FormGroup>
```

FormGroup không tự render input — caller kiểm soát phần input bên trong (Input, Select, Textarea từ atoms).

## States

- **Default**: chỉ label + input
- **Helper**: label + input + helper text (text-xs text-text-3)
- **Error**: label + input + error message (icon + text-danger) — helper bị ẩn
- **Required**: dấu `*` đỏ sau label

## Usage

```tsx
import { FormGroup, Input } from '@cowork/ui';

<FormGroup
  label="Email khách hàng"
  htmlFor="email"
  required
  helper="Email dùng để gửi xác nhận đơn hàng"
  error={errors.email}
>
  <Input id="email" type="email" value={email} onChange={setEmail} />
</FormGroup>
```

## Real-world example

- An Nhien checkout form: FormGroup cho mỗi field (họ tên · sđt · địa chỉ · ghi chú đơn), helper cho format sđt, error khi validate fail.
- KHI booking form: FormGroup cho field "Số điện thoại" với helper "Dùng số đã đăng ký member", error nếu số không match DB.
- A-Kryphan contact: FormGroup wrap textarea "Nội dung", error khi empty submit.

## Accessibility

- Label `<label htmlFor>` link đúng tới input qua `id`
- Error có `<svg aria-hidden>` + text — screen reader đọc text
- Required `*` chỉ là visual — để full a11y nên thêm `aria-required="true"` trên input bên trong
- Khi có error, nên set `aria-invalid="true"` + `aria-describedby={errorId}` trên input (caller responsibility)

## Responsive behavior

`flex flex-col gap-1.5` — stack dọc cố định mọi screen. Caller có thể grid 2-col FormGroup ngoài cùng để có form 2 cột.

## Related

- [[Card]] — wrap form trong Card.Body để có visual container
- [[Modal]] — form inline trong Modal cho CRUD popup

## Source

`react/src/molecules/FormGroup.tsx`
