mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix(ui): keep the savings header one shape across both tabs
Title, legend, toggle and subtitle all shared a row. The subtitle is longer on Cumulative than on Per day, so it wrapped on one tab and not the other, growing the header by a line and shifting the legend, the toggle and the chart with it. The title and the controls now hold a fixed row and the subtitle sits on its own line beneath, so nothing above the chart depends on how long that text is. The test that guards this selected the subtitle by class and matched a summary card's hint instead, which made it pass with the subtitle moved back into the row. It now finds the element by its text.
This commit is contained in:
parent
e9b4f956c0
commit
669fd84aee
2 changed files with 39 additions and 30 deletions
|
|
@ -249,31 +249,40 @@ 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.
|
||||
it("keeps the header the same shape on both tabs so the chart cannot shift", async () => {
|
||||
// The subtitle differs in length between the tabs ("Running total saved" vs "Saved
|
||||
// per day"). Sharing a row with the title and controls made the header grow a line
|
||||
// when it wrapped, moving the legend, the toggle and the chart below it.
|
||||
const { getByRole, getByTestId, container } = renderWith(twoDays());
|
||||
|
||||
const controlsOn = () => {
|
||||
const layout = () => {
|
||||
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
|
||||
// the toggle travels with the legend, so neither can move independently
|
||||
expect(controls.contains(getByRole("tablist"))).toBe(true);
|
||||
return controls;
|
||||
const titleRow = controls.parentElement as HTMLElement;
|
||||
// by text, not by class: several cards render a muted <p>, and grabbing the first
|
||||
// one silently asserts against a summary-card hint instead of this subtitle
|
||||
const subtitle = Array.from(container.querySelectorAll("p")).find((el) =>
|
||||
/Running total saved|Saved per day/.test(el.textContent ?? ""),
|
||||
);
|
||||
expect(subtitle, "savings subtitle should be rendered").toBeTruthy();
|
||||
return { controls, titleRow, subtitle: subtitle as HTMLElement };
|
||||
};
|
||||
|
||||
const cumulative = controlsOn();
|
||||
expect(cumulative.className).toContain("shrink-0");
|
||||
|
||||
const header = cumulative.parentElement as HTMLElement;
|
||||
expect(header.className).not.toContain("flex-wrap");
|
||||
const before = layout();
|
||||
// the subtitle is a sibling BELOW the title row, never inside it, so its length
|
||||
// cannot change that row's height
|
||||
expect(before.titleRow.contains(before.subtitle)).toBe(false);
|
||||
expect(before.titleRow.className).not.toContain("flex-wrap");
|
||||
expect(before.controls.className).toContain("shrink-0");
|
||||
|
||||
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");
|
||||
const after = layout();
|
||||
expect(after.controls).toBe(before.controls);
|
||||
expect(after.titleRow).toBe(before.titleRow);
|
||||
expect(after.titleRow.contains(after.subtitle)).toBe(false);
|
||||
expect(container.textContent).toContain("Saved per day");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -210,23 +210,23 @@ 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 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">
|
||||
{/* Title and controls share a fixed row; the subtitle gets its own line below.
|
||||
Competing for one row made the header taller whenever the subtitle wrapped,
|
||||
which differs between the two tabs, so the chart shifted down on one of them */}
|
||||
<CardHeader className="space-y-1.5">
|
||||
<div className="flex flex-row items-center justify-between gap-4">
|
||||
<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 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>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{savingsSubtitle}</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{accumulation === "cumulative" ? (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue