From 62523c1734ef27d648f58614dff9562f970dfcd2 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Thu, 13 Aug 2026 17:27:26 -0700 Subject: [PATCH] fix(ui): restore pattern search and popup layering in the guardrail wizard Two behaviour differences the port introduced, both found in browser QA The antd Select matched a prebuilt pattern on its display_name and its internal name; a Base UI Combobox only searches itemToStringLabel, so queries like "amex" and "sg" stopped matching. Restore the second field with a filter predicate on the Root, covered by a regression test that searches on a token the visible label does not contain Base UI portals a popup into a positioner whose "isolate z-50" is fixed in the primitive, so inside an antd Modal at z-index 1000 the options were visible but not clickable. antd hid this because its own dropdowns and tooltips already sat above its Modal. The positioner is not reachable from the call site, so this needs one app-wide rule keyed on an antd modal being present, and it becomes deletable when the last one goes --- .../content_filter/PatternModal.test.tsx | 14 ++++++++++++++ .../_components/content_filter/PatternModal.tsx | 6 ++++++ ui/litellm-dashboard/src/app/globals.css | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.test.tsx index 77cbcf6211d..5302ac3b7f7 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.test.tsx @@ -84,6 +84,20 @@ describe("PatternModal", () => { expect(screen.queryByText("AWS access key")).not.toBeInTheDocument(); }); + it("should match the internal pattern name when it is absent from the display name", async () => { + const user = userEvent.setup(); + renderModal(); + + expect(await screen.findByText("Add prebuilt pattern")).toBeInTheDocument(); + + await user.click(screen.getAllByRole("combobox")[0]); + await user.keyboard("ssn"); + + expect(await screen.findByText("US Social Security Number")).toBeInTheDocument(); + expect(screen.queryByText("Email address")).not.toBeInTheDocument(); + expect(screen.queryByText("Visa card")).not.toBeInTheDocument(); + }); + it("should report the chosen action", async () => { const user = userEvent.setup(); renderModal(); diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.tsx index df87c511cf4..2d98b67cb4a 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails/_components/content_filter/PatternModal.tsx @@ -28,6 +28,11 @@ interface PatternGroup { items: PrebuiltPattern[]; } +const matchesPatternQuery = (pattern: PrebuiltPattern, query: string) => { + const needle = query.toLowerCase(); + return pattern.display_name.toLowerCase().includes(needle) || pattern.name.toLowerCase().includes(needle); +}; + interface PatternModalProps { visible: boolean; prebuiltPatterns: PrebuiltPattern[]; @@ -74,6 +79,7 @@ const PatternModal: React.FC = ({ value={selectedPattern} onValueChange={(pattern: PrebuiltPattern | null) => pattern && onPatternNameChange(pattern.name)} itemToStringLabel={(pattern: PrebuiltPattern) => pattern.display_name} + filter={matchesPatternQuery} > diff --git a/ui/litellm-dashboard/src/app/globals.css b/ui/litellm-dashboard/src/app/globals.css index 97f61a610d9..7013e502c22 100644 --- a/ui/litellm-dashboard/src/app/globals.css +++ b/ui/litellm-dashboard/src/app/globals.css @@ -242,3 +242,13 @@ [data-slot="dialog-content"][data-nested-dialog-open] { visibility: hidden; } + +/* Base UI portals a popup into an `isolate z-50` positioner, which an antd Modal at z-index 1000 + then paints over, so options on a half-migrated page are visible but not clickable. The + positioner is not reachable from the call site, hence the parent selector. Delete this once no + route renders an antd Modal. */ +body:has(.ant-modal-wrap) div:has(> [data-slot="select-content"]), +body:has(.ant-modal-wrap) div:has(> [data-slot="combobox-content"]), +body:has(.ant-modal-wrap) div:has(> [data-slot="tooltip-content"]) { + z-index: 1100; +}