diff --git a/apps/app/src/components/template-library/template-card-pie.test.tsx b/apps/app/src/components/template-library/template-card-pie.test.tsx new file mode 100644 index 0000000..63305d8 --- /dev/null +++ b/apps/app/src/components/template-library/template-card-pie.test.tsx @@ -0,0 +1,34 @@ +import { afterEach, expect, it, vi } from "vitest"; +import { cleanup, render } from "@testing-library/react"; +import { TemplateCard } from "./template-card"; +afterEach(cleanup); +function preview(data: { label: string; value: number }[]) { + return render() + .container.querySelector("svg")!; +} +it.each([ + [[{ label: "Only category", value: 10 }], "#3b82f6"], + [[{ label: "Empty", value: 0 }, { label: "All revenue", value: 10 }, { label: "Other", value: 0 }], "#8b5cf6"], +] as const)("draws a full disk when one category represents the entire total", (data, color) => { + const svg = preview([...data]); + const disk = svg.querySelector("circle"); + expect(disk).not.toBeNull(); + expect(Number(disk!.getAttribute("cx"))).toBe(60); + expect(Number(disk!.getAttribute("cy"))).toBe(60); + expect(Number(disk!.getAttribute("r"))).toBe(50); + expect(disk!.getAttribute("fill")).toBe(color); +}); +it("retains separate slices for multiple positive categories", () => { + const svg = preview([{ label: "First", value: 3 }, { label: "Second", value: 1 }]); + expect(svg.querySelectorAll("path")).toHaveLength(2); + expect(svg.querySelector("circle")).toBeNull(); +}); + +it.each([ + { data: [] }, + { data: [{ label: "Only empty category", value: 0 }] }, + { data: [{ label: "First", value: 0 }, { label: "Second", value: 0 }] }, +])("keeps empty and zero-total previews unpainted", ({ data }) => { + expect(preview(data)).toBeNull(); +}); diff --git a/apps/app/src/components/template-library/template-card.tsx b/apps/app/src/components/template-library/template-card.tsx index 23b9824..2c0cfbd 100644 --- a/apps/app/src/components/template-library/template-card.tsx +++ b/apps/app/src/components/template-library/template-card.tsx @@ -67,6 +67,10 @@ function PieChartPreview({ data }: { data: { label: string; value: number }[] }) return acc; }, []); const slices = data.map((d, i) => { + // A full SVG arc with coincident endpoints has no painted area. + if (d.value === total) { + return ; + } const startAngle = ((cumulativeAngles[i] - d.value) / total) * 2 * Math.PI - Math.PI / 2; const endAngle = (cumulativeAngles[i] / total) * 2 * Math.PI - Math.PI / 2; const largeArc = d.value / total > 0.5 ? 1 : 0;