diff --git a/apps/loopover-ui/src/routes/app.index.test.tsx b/apps/loopover-ui/src/routes/app.index.test.tsx new file mode 100644 index 0000000000..3048a714fa --- /dev/null +++ b/apps/loopover-ui/src/routes/app.index.test.tsx @@ -0,0 +1,41 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { TooltipProvider } from "@/components/ui/tooltip"; +import { SparkStat } from "./app.index"; + +// #6984: SparkStat's loading branch hand-rolled its own animate-pulse divs instead of the shared +// Skeleton primitive every other loading placeholder in this app already uses. +describe("SparkStat loading state (#6984)", () => { + it("renders Skeleton placeholders (not the raw hand-rolled divs) while loading", () => { + const { container } = render( + , + ); + + expect(screen.getByRole("status", { name: "Loading Open PRs" })).toBeTruthy(); + // Skeleton renders animate-pulse blocks; the label/value/sparkline placeholders are 3 in total. + expect(container.querySelectorAll(".animate-pulse").length).toBe(3); + // The real label/value text never renders while loading. + expect(screen.queryByText("Open PRs")).toBeNull(); + expect(screen.queryByText("4")).toBeNull(); + }); + + it("renders the real label and value once data is available (not loading)", () => { + render( + + + , + ); + + expect(screen.getByText("Open PRs")).toBeTruthy(); + expect(screen.getByText("4")).toBeTruthy(); + expect(screen.queryByRole("status", { name: "Loading Open PRs" })).toBeNull(); + }); +}); diff --git a/apps/loopover-ui/src/routes/app.index.tsx b/apps/loopover-ui/src/routes/app.index.tsx index 89acdac9f0..5791de28fc 100644 --- a/apps/loopover-ui/src/routes/app.index.tsx +++ b/apps/loopover-ui/src/routes/app.index.tsx @@ -16,6 +16,7 @@ import { X, } from "lucide-react"; +import { Skeleton } from "@/components/ui/skeleton"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { StatusPill } from "@/components/site/control-primitives"; @@ -494,7 +495,7 @@ function OnboardingChecklist() { ); } -function SparkStat({ +export function SparkStat({ label, value, hint, @@ -518,10 +519,10 @@ function SparkStat({ aria-label={`Loading ${label}`} className="rounded-token border-hairline bg-card p-4" > -
+
-
-
+ +
);