docs(ui): expand bilingual component guides
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user