feat(lens): type to filter the traces agent dropdown (#44745)

* feat(lens): type to filter the traces agent dropdown

* test(lens): cover typing, Enter and clear in the agent filter
This commit is contained in:
ishaan-berri 2026-10-05 18:36:22 -07:00 • committed by GitHub
parent 6f5ca84f69
commit 6764b9af12
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 82 additions and 18 deletions

View file

@ -0,0 +1,55 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { withNuqsTestingAdapter, type UrlUpdateEvent } from "nuqs/adapters/testing";
import { describe, expect, it, vi } from "vitest";
import { runs } from "./__fixtures__/runs";
import { RunsToolbar } from "./RunsToolbar";
const agentBox = () => screen.getByRole("combobox", { name: "Filter traces by agent" });
describe("RunsToolbar", () => {
it("narrows the agent filter as you type and selects the match", async () => {
const user = userEvent.setup();
const onUrlUpdate = vi.fn<(event: UrlUpdateEvent) => void>();
render(<RunsToolbar query="" onQueryChange={vi.fn()} runs={runs} />, {
wrapper: withNuqsTestingAdapter({ onUrlUpdate }),
});
expect(agentBox()).toHaveAttribute("placeholder", "All agents");
await user.click(agentBox());
await user.keyboard("tri");
expect(screen.getAllByRole("option").map((o) => o.textContent)).toEqual(["triage"]);
await user.click(screen.getByRole("option", { name: "triage" }));
expect(onUrlUpdate.mock.lastCall?.[0].searchParams.get("agent")).toBe("triage");
});
it("picks the first match on Enter", async () => {
const user = userEvent.setup();
const onUrlUpdate = vi.fn<(event: UrlUpdateEvent) => void>();
render(<RunsToolbar query="" onQueryChange={vi.fn()} runs={runs} />, {
wrapper: withNuqsTestingAdapter({ onUrlUpdate }),
});
await user.click(agentBox());
await user.keyboard("res{Enter}");
expect(onUrlUpdate.mock.lastCall?.[0].searchParams.get("agent")).toBe("researcher");
});
it("says when no agent matches", async () => {
const user = userEvent.setup();
render(<RunsToolbar query="" onQueryChange={vi.fn()} runs={runs} />, { wrapper: withNuqsTestingAdapter() });
await user.click(agentBox());
await user.keyboard("zzz");
expect(screen.getByText("No matching agents")).toBeVisible();
});
it("clears back to all agents", async () => {
const user = userEvent.setup();
const onUrlUpdate = vi.fn<(event: UrlUpdateEvent) => void>();
render(<RunsToolbar query="" onQueryChange={vi.fn()} runs={runs} />, {
wrapper: withNuqsTestingAdapter({ searchParams: "?agent=triage", onUrlUpdate }),
});
expect(agentBox()).toHaveValue("triage");
await user.click(screen.getByRole("button", { name: "Clear" }));
expect(onUrlUpdate.mock.lastCall?.[0].searchParams.get("agent")).toBeNull();
});
});

View file

@ -3,6 +3,14 @@
import type { TraceSummary } from "../../types";
import type { TimeWindow } from "@/components/shared/timeline/Timeline";
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useRunFilterRouting } from "../../routing";
import { traceAgentNames } from "../../utils";
@ -20,12 +28,7 @@ interface RunsToolbarProps {
export function RunsToolbar({ query, onQueryChange, runs, range, children }: RunsToolbarProps) {
const { agent, status, setAgent, setStatus } = useRunFilterRouting();
const agents = [
{ value: "", label: "All agents" },
...[...new Set([...runs.flatMap(traceAgentNames), ...(agent ? [agent] : [])])]
.sort()
.map((name) => ({ value: name, label: name })),
];
const agents = [...new Set([...runs.flatMap(traceAgentNames), ...(agent ? [agent] : [])])].sort();
const statuses = [
{ value: "all", label: "All status" },
{ value: "ok", label: "No errors" },
@ -34,18 +37,24 @@ export function RunsToolbar({ query, onQueryChange, runs, range, children }: Run
return (
<div className="flex shrink-0 flex-wrap items-center gap-2 border-b bg-card p-2">
<RunSearch value={query} onChange={onQueryChange} runs={runs} range={range} />
<Select items={agents} value={agent} onValueChange={(value) => value !== null && setAgent(value)}>
<SelectTrigger size="sm" className="h-8 max-w-48 min-w-32 text-xs" aria-label="Filter traces by agent">
<SelectValue />
</SelectTrigger>
<SelectContent>
{agents.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
<Combobox items={agents} value={agent || null} onValueChange={(name) => setAgent(name ?? "")} autoHighlight>
<ComboboxInput
aria-label="Filter traces by agent"
placeholder="All agents"
showClear={!!agent}
className="h-8 w-48 text-xs"
/>
<ComboboxContent>
<ComboboxEmpty>No matching agents</ComboboxEmpty>
<ComboboxList>
{(name: string) => (
<ComboboxItem key={name} value={name} className="text-xs">
{name}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
<Select
items={statuses}
value={status}