diff --git a/packages/dashboard/app/components/ReliabilityView.css b/packages/dashboard/app/components/ReliabilityView.css index 5403ac7555..6d26d81d2d 100644 --- a/packages/dashboard/app/components/ReliabilityView.css +++ b/packages/dashboard/app/components/ReliabilityView.css @@ -1,9 +1,11 @@ .reliability-view { display: flex; + flex: 1 1 auto; flex-direction: column; gap: var(--space-lg); height: 100%; min-height: 0; + min-width: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; } @@ -17,20 +19,24 @@ .reliability-loading { display: flex; + flex: 1 1 auto; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-md); + min-width: 0; padding: var(--space-2xl); color: var(--text-muted); } .reliability-error { display: flex; + flex: 1 1 auto; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-md); + min-width: 0; padding: var(--space-2xl); text-align: center; } diff --git a/packages/dashboard/app/components/__tests__/ReliabilityView.test.tsx b/packages/dashboard/app/components/__tests__/ReliabilityView.test.tsx index 8d17c0dd21..9a1b981b3b 100644 --- a/packages/dashboard/app/components/__tests__/ReliabilityView.test.tsx +++ b/packages/dashboard/app/components/__tests__/ReliabilityView.test.tsx @@ -32,6 +32,27 @@ const baseResponse = { mergeAttempts: { mean: 1.2, max: 2, histogram: { "1": 1 } }, }; +function renderInProjectContent() { + return render( +
+ +
, + ); +} + +function setViewportWidth(width: number) { + Object.defineProperty(window, "innerWidth", { configurable: true, value: width }); + window.dispatchEvent(new Event("resize")); +} + +function expectFlexFill(element: HTMLElement) { + const computed = getComputedStyle(element); + expect(computed.flexGrow).toBe("1"); + expect(computed.flexShrink).toBe("1"); + expect(computed.flexBasis).toBe("auto"); + expect(computed.minWidth).toBe("0px"); +} + describe("ReliabilityView", () => { afterEach(() => { vi.useRealTimers(); @@ -58,6 +79,44 @@ describe("ReliabilityView", () => { expect(screen.queryByRole("heading", { name: "Reliability" })).not.toBeInTheDocument(); }); + it.each([ + ["desktop", 1024], + ["mobile", 375], + ])("fills flex parent width in %s loading state", (_label, width) => { + setViewportWidth(width); + vi.spyOn(globalThis, "fetch").mockReturnValue(new Promise(() => {})); + + renderInProjectContent(); + + expectFlexFill(screen.getByTestId("reliability-loading")); + }); + + it.each([ + ["desktop", 1024], + ["mobile", 375], + ])("fills flex parent width in %s populated state", async (_label, width) => { + setViewportWidth(width); + vi.spyOn(globalThis, "fetch").mockResolvedValue({ ok: true, json: async () => baseResponse } as Response); + + const { container } = renderInProjectContent(); + + await waitFor(() => expect(container.querySelector(".reliability-view")).not.toBeNull()); + expectFlexFill(container.querySelector(".reliability-view") as HTMLElement); + }); + + it.each([ + ["desktop", 1024], + ["mobile", 375], + ])("fills flex parent width in %s error state", async (_label, width) => { + setViewportWidth(width); + vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("Network unavailable")); + + renderInProjectContent(); + + await waitFor(() => expect(screen.getByTestId("reliability-error")).toBeInTheDocument()); + expectFlexFill(screen.getByTestId("reliability-error")); + }); + it("shows data after successful load even if loading refresh is pending", async () => { vi.useFakeTimers(); const fetchSpy = vi.spyOn(globalThis, "fetch")