mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
fix(ui): stop the savings legend and tab control moving between tabs
The card header was a wrapping flex row, and the subtitle is longer on
Cumulative ("Running total saved") than on Per day ("Saved per day"). The extra
width pushed the legend and the accumulation toggle onto a second row, so both
jumped whenever the tab changed.
`CardHeader` is now the row itself, the same structure `SummaryCard` in this file
already uses, with the controls held in a shrink-0 box so they keep their place
whatever the subtitle says. The other two headers in the file are title-only and
correctly stay plain.
This commit is contained in:
parent
e12415aac1
commit
e9b4f956c0
2 changed files with 44 additions and 15 deletions
|
|
@ -249,6 +249,34 @@ describe("UsageTab", () => {
|
|||
expect(readSeries(bars)[0]).toMatchObject({ "Auto-router": -0.05 });
|
||||
});
|
||||
|
||||
it("keeps the legend and the accumulation toggle in one place across both tabs", async () => {
|
||||
// The subtitle is longer on Cumulative ("Running total saved") than on Per day
|
||||
// ("Saved per day"). With a wrapping header the extra width pushed the legend and
|
||||
// the toggle onto a second row, so they jumped whenever the tab changed.
|
||||
const { getByRole, getByTestId, container } = renderWith(twoDays());
|
||||
|
||||
const controlsOn = () => {
|
||||
const legend = getByTestId("chart-legend");
|
||||
const controls = legend.parentElement as HTMLElement;
|
||||
// the toggle lives in the same box as the legend, so neither can move alone
|
||||
expect(controls.contains(getByRole("tablist"))).toBe(true);
|
||||
return controls;
|
||||
};
|
||||
|
||||
const cumulative = controlsOn();
|
||||
expect(cumulative.className).toContain("shrink-0");
|
||||
|
||||
const header = cumulative.parentElement as HTMLElement;
|
||||
expect(header.className).not.toContain("flex-wrap");
|
||||
|
||||
await userEvent.click(getByRole("tab", { name: "Per day" }));
|
||||
|
||||
// same container, same classes, after the subtitle changed length
|
||||
expect(controlsOn()).toBe(cumulative);
|
||||
expect((cumulative.parentElement as HTMLElement).className).not.toContain("flex-wrap");
|
||||
expect(container.textContent).toContain("Saved per day");
|
||||
});
|
||||
|
||||
it("subtracts a losing auto-router route from the total and keeps it out of the donut", () => {
|
||||
// Switching models leaves the new one with a cold cache, so a route can cost more
|
||||
// than the baseline would have. A negative slice is meaningless in a donut, but the
|
||||
|
|
|
|||
|
|
@ -210,21 +210,22 @@ const UsageTab: React.FC<UsageTabProps> = ({ accessToken, activity }) => {
|
|||
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
||||
<Card className="lg:col-span-2">
|
||||
<CardHeader>
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<CardTitle>Savings</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">{savingsSubtitle}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<CustomLegend categories={SAVINGS_SERIES} colors={SAVINGS_COLORS} />
|
||||
<Tabs value={accumulation} onValueChange={(value) => setAccumulation(value as SavingsAccumulation)}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="cumulative">Cumulative</TabsTrigger>
|
||||
<TabsTrigger value="per-interval">{intervalLabel}</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
{/* CardHeader is the row itself, matching SummaryCard: an inner wrapper that
|
||||
wrapped on overflow moved the legend and the tab control onto a second line
|
||||
whenever the subtitle grew, so they jumped between the two tabs */}
|
||||
<CardHeader className="flex flex-row items-start justify-between space-y-0 gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<CardTitle>Savings</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">{savingsSubtitle}</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-4">
|
||||
<CustomLegend categories={SAVINGS_SERIES} colors={SAVINGS_COLORS} />
|
||||
<Tabs value={accumulation} onValueChange={(value) => setAccumulation(value as SavingsAccumulation)}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="cumulative">Cumulative</TabsTrigger>
|
||||
<TabsTrigger value="per-interval">{intervalLabel}</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue