Skip to content

Commit 42b4048

Browse files
authored
Merge pull request #7 from ReactiveBayes/phase-charts-yaxis-zero
Anchor phase charts' Y axis at 0 via symlog
2 parents 2b6c2ae + 2480292 commit 42b4048

2 files changed

Lines changed: 73 additions & 2 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
import { render, screen } from "@testing-library/react";
3+
import userEvent from "@testing-library/user-event";
4+
import { PhaseBars } from "./PhaseBars";
5+
6+
// Assert the props handed to Recharts primitives (design/testing.md): the
7+
// YAxis stub publishes its `scale` and `domain` so we can verify the axis is
8+
// anchored at 0 across modes. Other primitives are pass-through stubs.
9+
vi.mock("recharts", async (importOriginal) => ({
10+
...(await importOriginal<typeof import("recharts")>()),
11+
// jsdom has zero layout size, so the real ResponsiveContainer renders nothing.
12+
ResponsiveContainer: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
13+
BarChart: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
14+
Bar: () => <div data-testid="bar" />,
15+
XAxis: () => <div data-testid="x-axis" />,
16+
Tooltip: () => <div data-testid="tooltip" />,
17+
YAxis: ({ scale, domain }: { scale?: unknown; domain?: unknown }) => (
18+
<div data-testid="y-axis" data-scale={String(scale)} data-domain={JSON.stringify(domain)} />
19+
),
20+
}));
21+
22+
const metricDefs = [
23+
{ id: "model_creation_ms", label: "Model creation", unit: "ms", lower_is_better: true },
24+
{ id: "cold_run_ms", label: "Cold run", unit: "ms", lower_is_better: true },
25+
];
26+
27+
const rows = [
28+
{ env: "1.12", model_creation_ms: 1, cold_run_ms: 100 },
29+
{ env: "1.11", model_creation_ms: 2, cold_run_ms: 120 },
30+
];
31+
32+
function renderBars() {
33+
return render(
34+
<PhaseBars
35+
rows={rows}
36+
xKey="env"
37+
metricDefs={metricDefs}
38+
ariaLabel="phase bars"
39+
captionPrefix="2 environments"
40+
emptyText="no phase data"
41+
/>,
42+
);
43+
}
44+
45+
function axis() {
46+
return screen.getByTestId("y-axis");
47+
}
48+
49+
describe("PhaseBars Y axis", () => {
50+
it("uses a pseudo-log (symlog) scale anchored at 0 by default", () => {
51+
renderBars();
52+
expect(axis()).toHaveAttribute("data-scale", "symlog");
53+
expect(JSON.parse(axis().getAttribute("data-domain")!)[0]).toBe(0);
54+
});
55+
56+
it("starts at 0 on the linear scale", async () => {
57+
const user = userEvent.setup();
58+
renderBars();
59+
await user.click(screen.getByRole("button", { name: /log scale/i }));
60+
expect(axis()).toHaveAttribute("data-scale", "linear");
61+
expect(JSON.parse(axis().getAttribute("data-domain")!)[0]).toBe(0);
62+
});
63+
64+
it("starts at 0 when stacked (forced linear)", async () => {
65+
const user = userEvent.setup();
66+
renderBars();
67+
await user.click(screen.getByRole("button", { name: /grouped/i }));
68+
expect(axis()).toHaveAttribute("data-scale", "linear");
69+
expect(JSON.parse(axis().getAttribute("data-domain")!)[0]).toBe(0);
70+
});
71+
});

frontend/src/components/charts/PhaseBars.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ export function PhaseBars({
133133
fontSize={11}
134134
tickLine={false}
135135
width={70}
136-
scale={effectiveLog ? "log" : "linear"}
137-
domain={effectiveLog ? ["auto", "auto"] : [0, "auto"]}
136+
scale={effectiveLog ? "symlog" : "linear"}
137+
domain={[0, "auto"]}
138138
tickFormatter={(value: number) => formatValue(value, "ms")}
139139
/>
140140
<Tooltip

0 commit comments

Comments
 (0)