Skip to content

Commit 21855f2

Browse files
RealDiligentjak-glitchcursoragent
authored
fix(ui-kit): render chart tooltip zero inside tabular-nums span (#10114)
Replace the truthiness guard on ChartTooltipContent value with an explicit nullish check so 0 and empty string go through toLocaleString in the formatted span instead of a bare React text child. Closes #10051 Co-authored-by: kai392 <chengjunkai4@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 88b81a2 commit 21855f2

2 files changed

Lines changed: 87 additions & 2 deletions

File tree

packages/loopover-ui-kit/src/components/chart.test.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,91 @@ describe("ChartTooltipContent (recharts v3, #8610)", () => {
125125
expect(chart.getByText("$120")).toBeTruthy();
126126
});
127127

128+
// #10051: a numeric 0 is a real data point — the old `item.value &&` guard rendered it as a bare
129+
// React text child (losing tabular-nums / toLocaleString). Pin both arms of the nullish guard and
130+
// assert the digit sits inside the formatted span, not beside it.
131+
it("REGRESSION (#10051): zero value renders inside the tabular-nums span, not as a bare text child", () => {
132+
const zeroPayload = [
133+
{
134+
dataKey: "revenue",
135+
name: "revenue",
136+
value: 0,
137+
color: "#0ea5e9",
138+
payload: { month: "Jan", revenue: 0 },
139+
},
140+
];
141+
const chart = renderInChart(
142+
<ChartTooltipContent active payload={zeroPayload} label="Jan" />,
143+
);
144+
145+
const valueSpan = chart.slot().querySelector("span.tabular-nums");
146+
expect(valueSpan).not.toBeNull();
147+
expect(valueSpan!.className).toContain("font-mono");
148+
expect(valueSpan!.className).toContain("tabular-nums");
149+
expect(valueSpan!.textContent).toBe("0");
150+
151+
// The formatted "0" must be a descendant of the span — not a direct text child of the row.
152+
const row = valueSpan!.closest(".flex.w-full");
153+
expect(row).not.toBeNull();
154+
const directZero = Array.from(row!.childNodes).some(
155+
(node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim() === "0",
156+
);
157+
expect(directZero).toBe(false);
158+
expect(chart.getByText("Revenue")).toBeTruthy();
159+
});
160+
161+
it("omits the value span only when value is undefined or null (#10051)", () => {
162+
const undefinedPayload = [
163+
{
164+
dataKey: "revenue",
165+
name: "revenue",
166+
value: undefined,
167+
color: "#0ea5e9",
168+
payload: { month: "Jan", revenue: 0 },
169+
},
170+
];
171+
const missing = renderInChart(
172+
<ChartTooltipContent active payload={undefinedPayload} label="Jan" />,
173+
);
174+
expect(missing.slot().textContent).toContain("Revenue");
175+
expect(missing.slot().querySelector("span.tabular-nums")).toBeNull();
176+
missing.unmount();
177+
178+
const nullPayload = [
179+
{
180+
dataKey: "revenue",
181+
name: "revenue",
182+
value: null,
183+
color: "#0ea5e9",
184+
payload: { month: "Jan", revenue: 0 },
185+
},
186+
];
187+
const nullish = renderInChart(
188+
<ChartTooltipContent active payload={nullPayload as typeof payload} label="Jan" />,
189+
);
190+
expect(nullish.slot().textContent).toContain("Revenue");
191+
expect(nullish.slot().querySelector("span.tabular-nums")).toBeNull();
192+
});
193+
194+
it("renders an empty-string value through the tabular-nums span (#10051)", () => {
195+
const emptyPayload = [
196+
{
197+
dataKey: "revenue",
198+
name: "revenue",
199+
value: "",
200+
color: "#0ea5e9",
201+
payload: { month: "Jan", revenue: "" },
202+
},
203+
];
204+
const chart = renderInChart(
205+
<ChartTooltipContent active payload={emptyPayload} label="Jan" />,
206+
);
207+
const valueSpan = chart.slot().querySelector("span.tabular-nums");
208+
expect(valueSpan).not.toBeNull();
209+
expect(valueSpan!.textContent).toBe("");
210+
expect(chart.getByText("Revenue")).toBeTruthy();
211+
});
212+
128213
it("resolves a series by nameKey when the payload's own key is not the config key", () => {
129214
const chart = renderInChart(
130215
<ChartTooltipContent

packages/loopover-ui-kit/src/components/chart.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,11 @@ const ChartTooltipContent = React.forwardRef<
249249
{itemConfig?.label || item.name}
250250
</span>
251251
</div>
252-
{item.value && (
252+
{item.value !== undefined && item.value !== null ? (
253253
<span className="font-mono font-medium tabular-nums text-foreground">
254254
{item.value.toLocaleString()}
255255
</span>
256-
)}
256+
) : null}
257257
</div>
258258
</>
259259
)}

0 commit comments

Comments
 (0)