docs(ui): expand bilingual component guides

This commit is contained in:
Maofeng
2026-09-21 17:27:45 +08:00
parent fb985edb20
commit 4c61e79d0d
22 changed files with 1636 additions and 192 deletions
+81 -20
View File
@@ -1,37 +1,98 @@
---
title: Compose a form
description: Combine field, input, and button primitives while keeping domain state in the application.
title: Compose a profile form
description: Combine Field, Input, Select, and Button into an accessible form while keeping state in the application.
order: 30
toc:
- id: create-the-form
title: Create the form
- id: complete-example
title: Complete example
- id: validation
title: Validation
title: Validation state
- id: controlled-select
title: Controlled Select
- id: styling
title: Styling
title: Style overrides
---
## Create the form {#create-the-form}
## Complete example {#complete-example}
```tsx
import * as React from "react"
import { Button } from "@workspace/ui/components/button"
import { Field, FieldError, FieldLabel } from "@workspace/ui/components/field"
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@workspace/ui/components/field"
import { Input } from "@workspace/ui/components/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@workspace/ui/components/select"
;<form onSubmit={saveProfile}>
<Field>
<FieldLabel htmlFor="display-name">Display name</FieldLabel>
<Input id="display-name" name="displayName" />
<FieldError>{errors.displayName}</FieldError>
</Field>
<Button type="submit">Save profile</Button>
</form>
export function ProfileForm() {
const [role, setRole] = React.useState("editor")
const [error, setError] = React.useState("")
function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
const data = new FormData(event.currentTarget)
setError(data.get("displayName") ? "" : "Enter a display name")
}
return (
<form onSubmit={submit} className="max-w-md space-y-6">
<FieldGroup>
<Field data-invalid={Boolean(error)}>
<FieldLabel htmlFor="display-name">Display name</FieldLabel>
<Input
id="display-name"
name="displayName"
aria-invalid={Boolean(error)}
aria-describedby="display-name-error"
/>
<FieldDescription>
This name is visible to other members.
</FieldDescription>
<FieldError id="display-name-error">{error}</FieldError>
</Field>
<Field>
<FieldLabel htmlFor="role">Role</FieldLabel>
<Select
value={role}
onValueChange={(value) => setRole(value ?? "editor")}
>
<SelectTrigger id="role">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="viewer">Viewer</SelectItem>
<SelectItem value="editor">Editor</SelectItem>
<SelectItem value="admin">Administrator</SelectItem>
</SelectContent>
</Select>
</Field>
</FieldGroup>
<Button type="submit">Save profile</Button>
</form>
)
}
```
## Validation {#validation}
## Validation state {#validation}
The UI package renders validation state but does not select a form library or schema. Connect native form data, React state, or a form framework at the application boundary.
The UI package does not select a validation library. The application produces errors and connects the same state to `Field data-invalid`, the control's `aria-invalid`, and `FieldError`. Multiple schema errors can be passed through `FieldError errors={errors}`; the component deduplicates them before rendering a list.
## Styling {#styling}
## Controlled Select {#controlled-select}
Use component variants for supported semantic changes and `className` for local layout. Use `cn` from `@workspace/ui/lib/utils` when conditional classes or consumer overrides must be merged.
The local Select is built on Base UI and uses `value` with `onValueChange`. `SelectTrigger` renders its own arrow, so it only needs `SelectValue`; do not add another chevron. The popup aligns to the trigger and adopts its width by default. Adjust `side`, `align`, `sideOffset`, and `alignItemWithTrigger` on `SelectContent` when required.
## Style overrides {#styling}
Use the existing `variant`, `size`, and structural parts first, then apply page layout through `className`. Components merge classes with `cn`, allowing consumer Tailwind classes to override default utilities. Promote overrides repeated across pages into a new variant or composite component instead of copying long class strings.