From 173e92ea24b403b7390202ed78f3fb135e88c796 Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Fri, 4 Sep 2026 08:37:08 +0000
Subject: [PATCH] docs(dify-ui): document combobox chip keyboard hints (#41815)
---
packages/dify-ui/docs/selection.md | 6 +++
.../dify-ui/src/combobox/index.stories.tsx | 54 ++++++++++++++-----
2 files changed, 46 insertions(+), 14 deletions(-)
diff --git a/packages/dify-ui/docs/selection.md b/packages/dify-ui/docs/selection.md
index 16a2a397a88..b0ad5a45e60 100644
--- a/packages/dify-ui/docs/selection.md
+++ b/packages/dify-ui/docs/selection.md
@@ -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.
diff --git a/packages/dify-ui/src/combobox/index.stories.tsx b/packages/dify-ui/src/combobox/index.stories.tsx
index 7b93bff4fe8..194042a1fe9 100644
--- a/packages/dify-ui/src/combobox/index.stories.tsx
+++ b/packages/dify-ui/src/combobox/index.stories.tsx
@@ -832,24 +832,31 @@ const MultipleChipsDemo = () => {
Reviewers
-
- >
- {(selectedValue) => (
-
- {selectedValue?.map((item) => (
-
+ >
+ {(selectedValue) => {
+ const selectedReviewers = selectedValue ?? []
+
+ return (
+ 0 ? 'Selected reviewers' : undefined}
+ >
+ {selectedReviewers.map((item) => (
+
{item.label}
))}
0 ? '' : 'Assign reviewers…'}
className="min-w-24 px-1 py-0.5"
/>
-
- )}
-
-
+
+ )
+ }}
+
@@ -861,6 +868,11 @@ const MultipleChipsDemo = () => {
Selected reviewers wrap inside the input instead of scrolling horizontally.
+ {value.length > 0 && (
+
+ {` ${value.length} selected. From the start of the input, press Left Arrow to focus the selected items`}
+
+ )}
)
@@ -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',
+ )
},
}