From 3e9820fa8dd66344b6fc2e3ca22d103e852b3333 Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:03:32 +0530 Subject: [PATCH] fix line chart's no data calculations and update tests (#16441) closes https://discord.com/channels/1130383047699738754/1447976382528360520 --- .../components/GraphWidgetBarChart.tsx | 5 +- .../components/GraphWidgetLineChart.tsx | 6 +- .../computeEffectiveValueRange.test.ts | 136 +++++++++++++----- .../graph/utils/computeEffectiveValueRange.ts | 5 - 4 files changed, 104 insertions(+), 48 deletions(-) diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx index 94eb102a1c..9172241f5e 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx @@ -132,13 +132,14 @@ export const GraphWidgetBarChart = ({ ? calculateStackedBarChartValueRange(data, keys) : calculateValueRangeFromBarChartKeys(data, keys); - const { effectiveMinimumValue, effectiveMaximumValue, hasNoData } = + const hasNoData = data.length === 0; + + const { effectiveMinimumValue, effectiveMaximumValue } = computeEffectiveValueRange({ calculatedMinimum: calculatedValueRange.minimum, calculatedMaximum: calculatedValueRange.maximum, rangeMin, rangeMax, - dataLength: data.length, }); const tickConfig = getBarChartTickConfig({ diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx index a483c1e628..10dd7f2778 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx @@ -110,13 +110,15 @@ export const GraphWidgetLineChart = ({ const calculatedValueRange = calculateValueRangeFromLineChartSeries(data); - const { effectiveMinimumValue, effectiveMaximumValue, hasNoData } = + const hasNoData = + data.length === 0 || data.every((series) => series.data.length === 0); + + const { effectiveMinimumValue, effectiveMaximumValue } = computeEffectiveValueRange({ calculatedMinimum: calculatedValueRange.minimum, calculatedMaximum: calculatedValueRange.maximum, rangeMin, rangeMax, - dataLength: data.length, }); const { enrichedSeries, nivoData, colors, legendItems } = useLineChartData({ diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/computeEffectiveValueRange.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/computeEffectiveValueRange.test.ts index 01c850d5cb..0772fd2ec7 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/computeEffectiveValueRange.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/computeEffectiveValueRange.test.ts @@ -1,59 +1,117 @@ import { computeEffectiveValueRange } from '../computeEffectiveValueRange'; describe('computeEffectiveValueRange', () => { - it('should return hasNoData true when dataLength is 0', () => { - const result = computeEffectiveValueRange({ - calculatedMinimum: 0, - calculatedMaximum: 0, - dataLength: 0, + describe('minimum value calculation', () => { + it('should start from 0 for non-negative values', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 5, + calculatedMaximum: 100, + }); + + expect(result.effectiveMinimumValue).toBe(0); }); - expect(result.hasNoData).toBe(true); + it('should use calculated minimum for negative values', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: -50, + calculatedMaximum: 100, + }); + + expect(result.effectiveMinimumValue).toBe(-50); + }); + + it('should use explicit rangeMin when provided', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 0, + calculatedMaximum: 100, + rangeMin: 25, + }); + + expect(result.effectiveMinimumValue).toBe(25); + }); }); - it('should start from 0 for non-negative values', () => { - const result = computeEffectiveValueRange({ - calculatedMinimum: 5, - calculatedMaximum: 100, - dataLength: 10, + describe('maximum value calculation', () => { + it('should add 10% padding for non-negative values without explicit rangeMax', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 0, + calculatedMaximum: 100, + }); + + expect(result.effectiveMaximumValue).toBe(110); }); - expect(result.effectiveMinimumValue).toBe(0); - expect(result.hasNoData).toBe(false); + it('should use minimum padding of 1 for small values', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 0, + calculatedMaximum: 5, + }); + + expect(result.effectiveMaximumValue).toBe(6); + }); + + it('should not add padding when rangeMax is explicit', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 0, + calculatedMaximum: 100, + rangeMax: 100, + }); + + expect(result.effectiveMaximumValue).toBe(100); + }); + + it('should not add padding when there are negative values', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: -50, + calculatedMaximum: 100, + }); + + expect(result.effectiveMaximumValue).toBe(100); + }); + + it('should use explicit rangeMax when provided', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 0, + calculatedMaximum: 100, + rangeMax: 50, + }); + + expect(result.effectiveMaximumValue).toBe(50); + }); }); - it('should use calculated minimum for negative values', () => { - const result = computeEffectiveValueRange({ - calculatedMinimum: -50, - calculatedMaximum: 100, - dataLength: 10, + describe('edge cases', () => { + it('should add padding when min equals max and no explicit range', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 50, + calculatedMaximum: 50, + }); + + expect(result.effectiveMaximumValue).toBeGreaterThan( + result.effectiveMinimumValue, + ); }); - expect(result.effectiveMinimumValue).toBe(-50); - }); + it('should not add padding when min equals max but explicit range is provided', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 50, + calculatedMaximum: 50, + rangeMin: 0, + rangeMax: 100, + }); - it('should respect explicit rangeMin and rangeMax', () => { - const result = computeEffectiveValueRange({ - calculatedMinimum: 0, - calculatedMaximum: 100, - rangeMin: 10, - rangeMax: 50, - dataLength: 10, + expect(result.effectiveMinimumValue).toBe(0); + expect(result.effectiveMaximumValue).toBe(100); }); - expect(result.effectiveMinimumValue).toBe(10); - expect(result.effectiveMaximumValue).toBe(50); - }); + it('should handle zero values', () => { + const result = computeEffectiveValueRange({ + calculatedMinimum: 0, + calculatedMaximum: 0, + }); - it('should add padding when min equals max and no explicit range', () => { - const result = computeEffectiveValueRange({ - calculatedMinimum: 50, - calculatedMaximum: 50, - dataLength: 1, + expect(result.effectiveMinimumValue).toBe(0); + expect(result.effectiveMaximumValue).toBe(1); }); - - expect(result.effectiveMaximumValue).toBeGreaterThan( - result.effectiveMinimumValue, - ); }); }); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/computeEffectiveValueRange.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/computeEffectiveValueRange.ts index 1c84b8352e..970268a336 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/computeEffectiveValueRange.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/computeEffectiveValueRange.ts @@ -8,13 +8,11 @@ type ComputeEffectiveValueRangeParams = { calculatedMaximum: number; rangeMin?: number; rangeMax?: number; - dataLength: number; }; type EffectiveValueRangeResult = { effectiveMinimumValue: number; effectiveMaximumValue: number; - hasNoData: boolean; }; export const computeEffectiveValueRange = ({ @@ -22,11 +20,9 @@ export const computeEffectiveValueRange = ({ calculatedMaximum, rangeMin, rangeMax, - dataLength, }: ComputeEffectiveValueRangeParams): EffectiveValueRangeResult => { const hasOnlyNonNegativeValues = calculatedMinimum >= 0 && calculatedMaximum >= 0; - const hasNoData = dataLength === 0; const baseMinimumValue = isDefined(rangeMin) ? rangeMin @@ -60,6 +56,5 @@ export const computeEffectiveValueRange = ({ return { effectiveMinimumValue, effectiveMaximumValue, - hasNoData, }; };