mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
refactor(ui): show the log drawer's sidebar toggle only where it has a row
Putting the toggle in the drawer header unconditionally stranded it on its own line: the model row renders empty for a log that names no model or provider, so the chevron sat alone above the request id. The sidebar keeps the toggle whenever it is open, in its own header. Collapsed, the toggle moves into the drawer header and joins the model row, or the request id row when there is no model to join. Shared between both through SidebarToggle so the two call sites cannot drift.
This commit is contained in:
parent
b27a1a13a2
commit
20e6d6457a
4 changed files with 108 additions and 13 deletions
|
|
@ -0,0 +1,67 @@
|
|||
import { screen, within } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { render } from "../../../../tests/test-utils";
|
||||
import type { LogEntry } from "../columns";
|
||||
import { DrawerHeader } from "./DrawerHeader";
|
||||
|
||||
const logEntry = (overrides: Partial<LogEntry>): LogEntry =>
|
||||
({
|
||||
request_id: "170d64ea-69f0-431a-be72-332f8f78c18a",
|
||||
api_key: "key-1",
|
||||
team_id: "team-1",
|
||||
model: "gpt-4o",
|
||||
model_id: "model-1",
|
||||
custom_llm_provider: "openai",
|
||||
call_type: "acompletion",
|
||||
spend: 0.01,
|
||||
total_tokens: 10,
|
||||
prompt_tokens: 5,
|
||||
completion_tokens: 5,
|
||||
startTime: "2026-07-07T09:50:13Z",
|
||||
endTime: "2026-07-07T09:50:14Z",
|
||||
cache_hit: "false",
|
||||
messages: [],
|
||||
response: {},
|
||||
...overrides,
|
||||
}) as LogEntry;
|
||||
|
||||
const renderHeader = (log: LogEntry, isSidebarCollapsed: boolean) =>
|
||||
render(
|
||||
<DrawerHeader
|
||||
log={log}
|
||||
onClose={vi.fn()}
|
||||
onPrevious={vi.fn()}
|
||||
onNext={vi.fn()}
|
||||
isSidebarCollapsed={isSidebarCollapsed}
|
||||
onToggleSidebar={vi.fn()}
|
||||
statusLabel="Failure"
|
||||
statusColor="error"
|
||||
environment="default"
|
||||
/>,
|
||||
);
|
||||
|
||||
const expandToggle = () => screen.getByLabelText("Expand trace sidebar");
|
||||
|
||||
describe("DrawerHeader sidebar toggle", () => {
|
||||
it("stays out of the header while the sidebar owns it", () => {
|
||||
renderHeader(logEntry({}), false);
|
||||
|
||||
expect(screen.queryByLabelText("Expand trace sidebar")).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText("Collapse trace sidebar")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shares the model row once the sidebar is collapsed", () => {
|
||||
renderHeader(logEntry({}), true);
|
||||
|
||||
const row = expandToggle().parentElement as HTMLElement;
|
||||
expect(within(row).getByText("gpt-4o")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("falls back to the request id row when the log names no model", () => {
|
||||
renderHeader(logEntry({ model: "", custom_llm_provider: "" }), true);
|
||||
|
||||
const row = expandToggle().parentElement as HTMLElement;
|
||||
expect(within(row).getByText("170d64ea-69f0-431a-be72-332f8f78c18a")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState } from "react";
|
||||
import { Check, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Copy, X } from "lucide-react";
|
||||
import { Check, ChevronDown, ChevronUp, Copy, X } from "lucide-react";
|
||||
import moment from "moment";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -7,6 +7,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/comp
|
|||
import { LogEntry } from "../columns";
|
||||
import { AutoRouterTag } from "@/components/shared/table_cells";
|
||||
import { ClassifyTag } from "./ClassifyTag";
|
||||
import { SidebarToggle } from "./SidebarToggle";
|
||||
import { getProviderLogoAndName } from "../../provider_info_helpers";
|
||||
import {
|
||||
DRAWER_HEADER_PADDING,
|
||||
|
|
@ -47,6 +48,8 @@ export function DrawerHeader({
|
|||
}: DrawerHeaderProps) {
|
||||
const provider = log.custom_llm_provider || "";
|
||||
const providerInfo = provider ? getProviderLogoAndName(provider) : null;
|
||||
const showToggleWithProvider = isSidebarCollapsed && Boolean(providerInfo || log.model);
|
||||
const showToggleWithRequestId = isSidebarCollapsed && !showToggleWithProvider;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -61,15 +64,7 @@ export function DrawerHeader({
|
|||
>
|
||||
{/* Row 0: Model + Provider with Logo */}
|
||||
<div className="flex items-center gap-2" style={{ marginBottom: SPACING_MEDIUM }}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
onClick={onToggleSidebar}
|
||||
className="shrink-0"
|
||||
aria-label={isSidebarCollapsed ? "Expand trace sidebar" : "Collapse trace sidebar"}
|
||||
>
|
||||
{isSidebarCollapsed ? <ChevronRight className="size-4" /> : <ChevronLeft className="size-4" />}
|
||||
</Button>
|
||||
{showToggleWithProvider && <SidebarToggle isCollapsed onToggle={onToggleSidebar} />}
|
||||
<ModelProviderSection
|
||||
model={log.model}
|
||||
modelGroup={log.model_group}
|
||||
|
|
@ -81,8 +76,15 @@ export function DrawerHeader({
|
|||
|
||||
{/* Row 1: Request ID + Actions */}
|
||||
<div
|
||||
style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: SPACING_MEDIUM }}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
gap: 4,
|
||||
marginBottom: SPACING_MEDIUM,
|
||||
}}
|
||||
>
|
||||
{showToggleWithRequestId && <SidebarToggle isCollapsed onToggle={onToggleSidebar} />}
|
||||
<RequestIdSection requestId={log.request_id} />
|
||||
<NavigationSection onPrevious={onPrevious} onNext={onNext} onClose={onClose} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { AGENT_CALL_TYPES, MCP_CALL_TYPES } from "../constants";
|
|||
import { getEventDisplayName } from "../utils";
|
||||
import { ClassifyTag } from "./ClassifyTag";
|
||||
import { DrawerHeader } from "./DrawerHeader";
|
||||
import { SidebarToggle } from "./SidebarToggle";
|
||||
import { useKeyboardNavigation } from "./useKeyboardNavigation";
|
||||
import { LogDetailContent, GuardrailJumpLink } from "./LogDetailContent";
|
||||
import { sessionSpendLogsCall } from "../../networking";
|
||||
|
|
@ -314,8 +315,12 @@ export function LogDetailsDrawer({
|
|||
<div style={{ height: "100%" }} className="flex">
|
||||
{!isSidebarCollapsed && (
|
||||
<div className="border-r border-border bg-muted flex flex-col shrink-0" style={{ width: SIDEBAR_WIDTH_PX }}>
|
||||
<div className="px-3 py-2 border-b border-border bg-card">
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-start gap-1 py-2 pl-1 pr-3 border-b border-border bg-card">
|
||||
<SidebarToggle
|
||||
isCollapsed={isSidebarCollapsed}
|
||||
onToggle={() => setIsSidebarCollapsed((collapsed) => !collapsed)}
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-[10px] uppercase tracking-wide text-muted-foreground">
|
||||
{isSessionMode ? "Session" : "Trace"}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export interface SidebarToggleProps {
|
||||
isCollapsed: boolean;
|
||||
onToggle: () => void;
|
||||
}
|
||||
|
||||
export function SidebarToggle({ isCollapsed, onToggle }: SidebarToggleProps) {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-xs"
|
||||
onClick={onToggle}
|
||||
className="shrink-0"
|
||||
aria-label={isCollapsed ? "Expand trace sidebar" : "Collapse trace sidebar"}
|
||||
>
|
||||
{isCollapsed ? <ChevronRight className="size-4" /> : <ChevronLeft className="size-4" />}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue