docs(dify-ui): document combobox chip keyboard hints (#41815)

This commit is contained in:
yyh 2026-09-04 08:37:08 +00:00 committed by GitHub
parent b5ee943a63
commit 173e92ea24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 46 additions and 14 deletions

View File

@ -17,6 +17,12 @@ domain value type through its public API.
Multiple-selection comboboxes follow the Base UI chips composition: chips and the input share the
input group, chips wrap, and the group grows vertically.
For chip-based multiple comboboxes, render `ComboboxValue` around `ComboboxChips` and label the
chips container only while it has the conditional `toolbar` role. Consumers must localize the
`ComboboxChip` Backspace/Delete description, the item-specific `ComboboxChipRemove` name, and the
input's selection count and Left Arrow hint. If `FieldDescription` already sets `aria-describedby`,
put the input hint there because it takes precedence over `aria-description`.
Autocomplete, Combobox, and Select popups use Base UI's `--anchor-width` and `--available-width`
variables to follow their trigger while clamping to the viewport. Do not replace that sizing with
a fixed width or an unclamped minimum width.

View File

@ -832,24 +832,31 @@ const MultipleChipsDemo = () => {
<FieldLabel>Reviewers</FieldLabel>
<Combobox items={reviewerOptions} multiple value={value} onValueChange={setValue}>
<ComboboxInputGroup className="h-auto min-h-8 items-start py-1">
<ComboboxChips>
<ComboboxValue<Option, true>>
{(selectedValue) => (
<React.Fragment>
{selectedValue?.map((item) => (
<ComboboxChip key={item.value}>
<ComboboxValue<Option, true>>
{(selectedValue) => {
const selectedReviewers = selectedValue ?? []
return (
<ComboboxChips
aria-label={selectedReviewers.length > 0 ? 'Selected reviewers' : undefined}
>
{selectedReviewers.map((item) => (
<ComboboxChip
key={item.value}
aria-description="Press Backspace or Delete to remove"
>
<span className="max-w-32 truncate">{item.label}</span>
<ComboboxChipRemove aria-label={`Remove ${item.label}`} />
</ComboboxChip>
))}
<ComboboxInput
placeholder={selectedValue?.length ? '' : 'Assign reviewers…'}
placeholder={selectedReviewers.length > 0 ? '' : 'Assign reviewers…'}
className="min-w-24 px-1 py-0.5"
/>
</React.Fragment>
)}
</ComboboxValue>
</ComboboxChips>
</ComboboxChips>
)
}}
</ComboboxValue>
</ComboboxInputGroup>
<ComboboxPortal>
<ComboboxPositioner>
@ -861,6 +868,11 @@ const MultipleChipsDemo = () => {
</Combobox>
<FieldDescription>
Selected reviewers wrap inside the input instead of scrolling horizontally.
{value.length > 0 && (
<span className="sr-only">
{` ${value.length} selected. From the start of the input, press Left Arrow to focus the selected items`}
</span>
)}
</FieldDescription>
</Field>
)
@ -871,11 +883,25 @@ export const MultipleChips: Story = {
play: async ({ canvas, userEvent }) => {
await expect(canvas.getByText('Maya Chen')).toBeVisible()
await expect(canvas.getByText('Liam Brooks')).toBeVisible()
await expect(canvas.getByRole('toolbar', { name: 'Selected reviewers' })).toBeVisible()
await userEvent.click(canvas.getByRole('button', { name: 'Remove Maya Chen' }))
await expect(canvas.getByText('Liam Brooks').parentElement!).toHaveAccessibleDescription(
'Press Backspace or Delete to remove',
)
await expect(canvas.queryByText('Maya Chen')).not.toBeInTheDocument()
await expect(canvas.getByText('Liam Brooks')).toBeVisible()
const input = canvas.getByRole('combobox', { name: 'Reviewers' })
await expect(input).toHaveAccessibleDescription(
'Selected reviewers wrap inside the input instead of scrolling horizontally. 2 selected. From the start of the input, press Left Arrow to focus the selected items',
)
input.focus()
await userEvent.keyboard('{ArrowLeft}{Delete}')
await expect(canvas.queryByText('Liam Brooks')).not.toBeInTheDocument()
await expect(canvas.getByText('Maya Chen')).toBeVisible()
await expect(input).toHaveAccessibleDescription(
'Selected reviewers wrap inside the input instead of scrolling horizontally. 1 selected. From the start of the input, press Left Arrow to focus the selected items',
)
},
}