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', + ) }, }