From 383b10f71efe987573e7a8c082d4c09d16d11abc Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Thu, 16 Apr 2026 17:44:26 +0100 Subject: [PATCH] fix(site): allow search by label in MultiSelectComboBox (#24421) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relates to https://linear.app/codercom/issue/CODAGT-103 - Add `keywords={[option.label]}` to `CommandItem` in `MultiSelectCombobox` so cmdk's default filter matches against the visible label text, not just the value (UUID) - Extend `OpenCombobox` story with type-to-filter assertions - Add "search filters by display name" step to `TemplateAllowlist` story > 🤖 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../MultiSelectCombobox.stories.tsx | 40 +++++++++++++++++-- .../MultiSelectCombobox.tsx | 1 + ...AgentSettingsTemplatesPageView.stories.tsx | 33 +++++++++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/site/src/components/MultiSelectCombobox/MultiSelectCombobox.stories.tsx b/site/src/components/MultiSelectCombobox/MultiSelectCombobox.stories.tsx index 29da4b4750..60d4e4eec9 100644 --- a/site/src/components/MultiSelectCombobox/MultiSelectCombobox.stories.tsx +++ b/site/src/components/MultiSelectCombobox/MultiSelectCombobox.stories.tsx @@ -31,11 +31,43 @@ export const Default: Story = {}; export const OpenCombobox: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getByPlaceholderText("Select organization")); + const input = canvas.getByPlaceholderText("Select organization"); + await userEvent.click(input); - await waitFor(() => - expect(canvas.getByText("My Organization")).toBeInTheDocument(), - ); + // Both options should be visible initially. + await waitFor(() => { + expect( + canvas.getByRole("option", { name: "My Organization" }), + ).toBeInTheDocument(); + expect( + canvas.getByRole("option", { name: "My Organization 2" }), + ).toBeInTheDocument(); + }); + + // Type a display name to filter — this verifies cmdk filters + // by label rather than by the underlying UUID value. + await userEvent.type(input, "My Organization 2"); + + await waitFor(() => { + expect( + canvas.getByRole("option", { name: "My Organization 2" }), + ).toBeInTheDocument(); + expect( + canvas.queryByRole("option", { name: /^My Organization$/ }), + ).not.toBeInTheDocument(); + }); + + // Clear the search and confirm both options reappear. + await userEvent.clear(input); + + await waitFor(() => { + expect( + canvas.getByRole("option", { name: "My Organization" }), + ).toBeInTheDocument(); + expect( + canvas.getByRole("option", { name: "My Organization 2" }), + ).toBeInTheDocument(); + }); }, }; diff --git a/site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx b/site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx index f1725acd12..6745e8aefb 100644 --- a/site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx +++ b/site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx @@ -642,6 +642,7 @@ export const MultiSelectCombobox: React.FC = ({ { e.preventDefault(); diff --git a/site/src/pages/AgentsPage/AgentSettingsTemplatesPageView.stories.tsx b/site/src/pages/AgentsPage/AgentSettingsTemplatesPageView.stories.tsx index 875d8ca4a8..025468e3dc 100644 --- a/site/src/pages/AgentsPage/AgentSettingsTemplatesPageView.stories.tsx +++ b/site/src/pages/AgentsPage/AgentSettingsTemplatesPageView.stories.tsx @@ -60,6 +60,39 @@ export const TemplateAllowlist: Story = { expect(saveBtn).toBeDisabled(); }); + await step("search filters by display name", async () => { + const input = canvas.getByPlaceholderText("Select templates..."); + await userEvent.click(input); + + // Type a partial display name. + await userEvent.type(input, "Docker"); + + // The matching template should be visible. + await waitFor(() => { + expect( + canvas.getByRole("option", { name: "Docker Development" }), + ).toBeVisible(); + }); + + // A non-matching template should not be visible. + await waitFor(() => { + expect( + canvas.queryByRole("option", { name: "Kubernetes Production" }), + ).not.toBeInTheDocument(); + }); + + // Clear the search and verify the full list returns. + await userEvent.clear(input); + await waitFor(() => { + expect( + canvas.getByRole("option", { name: "Kubernetes Production" }), + ).toBeVisible(); + }); + + // Close dropdown by pressing Escape so the next step starts clean. + await userEvent.keyboard("{Escape}"); + }); + await step("select one template and save", async () => { const input = canvas.getByPlaceholderText("Select templates..."); await userEvent.click(input);