From e314c701074d86ae1a39a1966b57b7283726ddf3 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 28 Nov 2023 12:29:40 +0100 Subject: [PATCH 1/2] fix(partition): zero value sectors cause max stack call --- .../src/chart_types/partition_chart/layout/utils/treemap.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/charts/src/chart_types/partition_chart/layout/utils/treemap.ts b/packages/charts/src/chart_types/partition_chart/layout/utils/treemap.ts index 9f1ec62684..9d26e6a6e9 100644 --- a/packages/charts/src/chart_types/partition_chart/layout/utils/treemap.ts +++ b/packages/charts/src/chart_types/partition_chart/layout/utils/treemap.ts @@ -29,11 +29,11 @@ function layVector( areaAccessor: (e: ArrayEntry) => number, ): LayoutElement { const area = nodes.reduce((p, n) => p + areaAccessor(n), 0); - const dependentSize = area / independentSize; // here we lose a bit of accuracy + const dependentSize = independentSize === 0 ? 0 : area / independentSize; // here we lose a bit of accuracy let currentOffset = 0; const sectionOffsets = [currentOffset]; const sectionSizes = nodes.map((e, i) => { - const sectionSize = areaAccessor(e) / dependentSize; // here we gain back a bit of accuracy + const sectionSize = dependentSize === 0 ? 0 : areaAccessor(e) / dependentSize; // here we gain back a bit of accuracy if (i < nodes.length - 1) sectionOffsets.push((currentOffset += sectionSize)); return sectionSize; }); From 849613360000df2ab75c974c9d67a0aa0084d4d4 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Tue, 28 Nov 2023 14:56:35 +0100 Subject: [PATCH 2/2] add test --- .../partition_chart/partition.test.tsx | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/charts/src/chart_types/partition_chart/partition.test.tsx b/packages/charts/src/chart_types/partition_chart/partition.test.tsx index feef8a404b..caad8cb57f 100644 --- a/packages/charts/src/chart_types/partition_chart/partition.test.tsx +++ b/packages/charts/src/chart_types/partition_chart/partition.test.tsx @@ -9,11 +9,13 @@ import { Store } from 'redux'; import { computeLegendSelector } from './state/selectors/compute_legend'; +import { partitionMultiGeometries } from './state/selectors/geometries'; import { getLegendItemsLabels } from './state/selectors/get_legend_items_labels'; import { MockGlobalSpec, MockSeriesSpec } from '../../mocks/specs'; import { MockStore } from '../../mocks/store'; import { GlobalChartState } from '../../state/chart_state'; import { LegendItemLabel } from '../../state/selectors/get_legend_items_labels'; +import { LIGHT_THEME } from '../../utils/themes/light_theme'; // sorting is useful to ensure tests pass even if order changes (where order doesn't matter) const ascByLabel = (a: LegendItemLabel, b: LegendItemLabel) => (a.label < b.label ? -1 : a.label > b.label ? 1 : 0); @@ -160,5 +162,31 @@ describe('Retain hierarchy even with arbitrary names', () => { ); expect(getLegendItemsLabels(store.getState()).map((l) => l.label)).toEqual([]); }); + it('avoid max stack call with zero value at specific dimensions', () => { + MockStore.updateDimensions(store, { width: 557, height: 360, top: 0, left: 0 }); + MockStore.addSpecs( + [ + MockGlobalSpec.settings({ showLegend: false, theme: LIGHT_THEME }), + MockSeriesSpec.treemap({ + data: [ + { cat: 'a', val: 1 }, + { cat: 'b', val: 1 }, + { cat: 'c', val: 0 }, + { cat: 'd', val: 1 }, + ], + valueAccessor: (d: { cat: string; val: number }) => d.val, + layers: [ + { + groupByRollup: (d: { cat: string; val: number }) => d.cat, + }, + ], + }), + ], + store, + ); + expect(() => { + partitionMultiGeometries(store.getState()); + }).not.toThrow(); + }); }); });