From a63319fea613f609cb5461e47f387a643a44c5ff Mon Sep 17 00:00:00 2001 From: Yxq Date: Tue, 20 Oct 2026 16:23:51 +0800 Subject: [PATCH] fix: keep empty cells empty when negating or formatting percentages ## Motivation Both formatters use `isNaN(Number(v))` to decide whether a value is numeric, but `Number('')` and `Number(null)` are both `0`, so an empty cell slips through: ```ts percentageValueFormat('') // '0' - an empty cell rendered as 0% formatNegativeNumber(true, ['']) // [-0] - an empty cell rendered as -0 formatNegativeNumber(true, [null])// [-0] ``` A missing data point therefore shows up as a real `0` (or `-0`) in the chart legend and tooltip, which reads as "measured as zero" rather than "no data". ## Change Return the value untouched when it is `''`, `null` or `undefined`, before the numeric path. ## Verification ``` # tests only Tests: 2 failed, 3 passed, 5 total # with the fix Tests: 5 passed, 5 total ``` Signed-off-by: Yxq --- apps/web/src/common/utils/format.test.tsx | 23 ++++++++++++++++++++++- apps/web/src/common/utils/format.tsx | 5 +++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/apps/web/src/common/utils/format.test.tsx b/apps/web/src/common/utils/format.test.tsx index 7b6f1ea559..067573b60b 100644 --- a/apps/web/src/common/utils/format.test.tsx +++ b/apps/web/src/common/utils/format.test.tsx @@ -1,4 +1,10 @@ -import { formatRepoName, shortenAxisLabel, roundedNum } from './format'; +import { + formatNegativeNumber, + formatRepoName, + percentageValueFormat, + shortenAxisLabel, + roundedNum, +} from './format'; describe('format', () => { it('formatRepoName', function () { @@ -32,4 +38,19 @@ describe('format', () => { expect(roundedNum(1.2345, 3)).toEqual(1.235); expect(roundedNum(100000)).toEqual(100000); }); + + it('leaves empty values alone when negating', function () { + expect(formatNegativeNumber(true, ['', null as any, '-', '5'])).toEqual([ + '', + null, + '-', + -5, + ]); + }); + + it('leaves empty values alone when formatting percentages', function () { + expect(percentageValueFormat('')).toBe(''); + expect(percentageValueFormat(null as any)).toBe(null); + expect(percentageValueFormat('0.5')).toBe(50); + }); }); diff --git a/apps/web/src/common/utils/format.tsx b/apps/web/src/common/utils/format.tsx index b684d448ba..ea27066b13 100644 --- a/apps/web/src/common/utils/format.tsx +++ b/apps/web/src/common/utils/format.tsx @@ -82,6 +82,10 @@ export const formatRepoName = ({ }; export const percentageValueFormat = (value: string | number) => { + // Number('') and Number(null) are 0, so an empty cell used to be printed as 0%. + if (value === '' || value === null || value === undefined) { + return value; + } if (!isNaN(Number(value))) { return toFixed(+value * 100, 3); } @@ -101,6 +105,7 @@ export const formatNegativeNumber = ( ) => { return condition ? data.map((v) => { + if (v === '' || v === null || v === undefined) return v; if (!isNaN(Number(v))) return -v; return v; })