Skip to content

Commit 89924b8

Browse files
authored
LG-4803: refactor echarts option configurations and fix exhaustive dependencies (#2812)
* Destructure chart and list exhaustive dependencies for Chart child components * Add and install @leafygreen-ui/hooks to charts core package * Refactor echarts options configuration and list exhaustive deps in useEchart and useChart * Changeset * Address feedback
1 parent d72935e commit 89924b8

23 files changed

+641
-492
lines changed

.changeset/tasty-trainers-brake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@lg-charts/core': patch
3+
---
4+
5+
[LG-4803](https://jira.mongodb.org/browse/LG-4803): fix exhaustive dependencies issues and refactor echarts options configuration

charts/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@dnd-kit/sortable": "^10.0.0",
1919
"@dnd-kit/utilities": "^3.2.2",
2020
"@leafygreen-ui/emotion": "workspace:^",
21+
"@leafygreen-ui/hooks": "workspace:^",
2122
"@leafygreen-ui/lib": "workspace:^",
2223
"@leafygreen-ui/palette": "workspace:^",
2324
"@leafygreen-ui/tokens": "workspace:^",

charts/core/src/Chart/config/getDefaultChartOptions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from '@leafygreen-ui/tokens';
1111

1212
import { ChartOptions } from '../Chart.types';
13+
import { TOOLBOX_ID, X_AXIS_ID, Y_AXIS_ID } from '../constants';
1314

1415
const commonAxisOptions = {
1516
/**
@@ -66,17 +67,20 @@ export const getDefaultChartOptions = (
6667
},
6768

6869
xAxis: {
70+
id: X_AXIS_ID,
6971
type: 'time',
7072
...commonAxisOptions,
7173
},
7274

7375
yAxis: {
76+
id: Y_AXIS_ID,
7477
type: 'value',
7578
...commonAxisOptions,
7679
},
7780

7881
// Sets up zooming
7982
toolbox: {
83+
id: TOOLBOX_ID,
8084
feature: {
8185
dataZoom: {
8286
show: true,

charts/core/src/Chart/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const TOOLBOX_ID = 'toolbox';
2+
export const X_AXIS_ID = 'x-axis';
3+
export const Y_AXIS_ID = 'y-axis';
Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,50 @@
11
import { ChartOptions } from '../Chart.types';
22

3-
import { addSeries, removeSeries, updateOptions } from './updateUtils';
3+
import {
4+
getOptionsToUpdateWithAddedSeries,
5+
getOptionsToUpdateWithRemovedSeries,
6+
} from './updateUtils';
47

58
describe('@lg-charts/core/Chart/hooks/updateUtils', () => {
6-
test('addSeries should add a series to the chart options', () => {
9+
test('getOptionsToUpdateWithAddedSeries should return options with an added series', () => {
710
const currentOptions: Partial<ChartOptions> = {
8-
series: [{ name: 'series1' }],
11+
series: [{ id: 'series-1' }],
912
};
10-
const newSeriesName = 'series2';
11-
const data = { name: newSeriesName };
12-
const updatedOptions = addSeries(currentOptions, data);
13+
const newSeriesId = 'series-2';
14+
const data = { id: newSeriesId };
15+
const updatedOptions = getOptionsToUpdateWithAddedSeries(
16+
currentOptions,
17+
data,
18+
);
1319
expect(updatedOptions.series).toHaveLength(2);
14-
expect(updatedOptions.series?.[1].name).toBe(newSeriesName);
20+
expect(updatedOptions.series?.[1].id).toBe(newSeriesId);
1521
});
1622

17-
test('addSeries should not add a series if a chart with the same name exists', () => {
23+
test('getOptionsToUpdateWithAddedSeries should return options without an added series if a series with the same id exists', () => {
1824
const currentOptions: Partial<ChartOptions> = {
19-
series: [{ name: 'series1' }],
25+
series: [{ id: 'series-1' }],
2026
};
21-
const newSeriesName = 'series1';
22-
const data = { name: newSeriesName };
23-
const updatedOptions = addSeries(currentOptions, data);
27+
const existingSeriesId = 'series-1';
28+
const data = { id: existingSeriesId };
29+
const updatedOptions = getOptionsToUpdateWithAddedSeries(
30+
currentOptions,
31+
data,
32+
);
2433
expect(updatedOptions.series).toHaveLength(1);
25-
expect(updatedOptions.series?.[0].name).toBe(newSeriesName);
34+
expect(updatedOptions.series?.[0].id).toBe(existingSeriesId);
2635
});
2736

28-
test('removeSeries should remove a series from the chart options', () => {
37+
test('getOptionsToUpdateWithRemovedSeries should return options with a removed series', () => {
2938
const currentOptions: Partial<ChartOptions> = {
30-
series: [{ name: 'series1' }, { name: 'series2' }],
39+
series: [{ id: 'series-1' }, { id: 'series-2' }],
3140
};
32-
const seriesName1 = 'series1';
33-
const seriesName2 = 'series2';
34-
const updatedOptions = removeSeries(currentOptions, seriesName1);
41+
const seriesId1 = 'series-1';
42+
const seriesId2 = 'series-2';
43+
const updatedOptions = getOptionsToUpdateWithRemovedSeries(
44+
currentOptions,
45+
seriesId1,
46+
);
3547
expect(updatedOptions.series).toHaveLength(1);
36-
expect(updatedOptions.series?.[0].name).toBe(seriesName2);
37-
});
38-
39-
/**
40-
* Tests that option updates don't overwrite the entire chart options object.
41-
*/
42-
test('updateOptions should merge chart options non-destructively', () => {
43-
const currentOptions: Partial<ChartOptions> = {
44-
xAxis: {
45-
show: true,
46-
splitLine: {
47-
show: true,
48-
},
49-
},
50-
};
51-
const updatedOptions = updateOptions(currentOptions, {
52-
xAxis: {
53-
show: false, // This should only update the show property and not other properties
54-
},
55-
grid: {
56-
show: true,
57-
},
58-
});
59-
// @ts-ignore: Property 'show' does not exist on type 'Arrayable<AriaOption>'.
60-
expect(updatedOptions?.xAxis?.show).toBe(false);
61-
// @ts-ignore: Property 'show' does not exist on type 'Arrayable<AriaOption>'.
62-
expect(updatedOptions?.xAxis?.splitLine?.show).toBe(true);
63-
// @ts-ignore: Property 'show' does not exist on type 'Arrayable<GridOption>'.
64-
expect(updatedOptions?.grid?.show).toBe(true);
48+
expect(updatedOptions.series?.[0].id).toBe(seriesId2);
6549
});
6650
});
Lines changed: 39 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,49 @@
1+
import { ECBasicOption } from 'echarts/types/dist/shared';
2+
13
import { ChartOptions, SeriesOption } from '../Chart.types';
24

3-
export function addSeries(
4-
currentOptions: ChartOptions,
5+
export function getOptionsToUpdateWithAddedSeries(
6+
currentOptions: ECBasicOption | undefined,
57
data: SeriesOption,
68
): Partial<ChartOptions> {
7-
const updatedOptions = { ...currentOptions };
8-
9-
if (!updatedOptions.series) {
10-
updatedOptions.series = [data];
11-
} else {
12-
if (!updatedOptions.series.some(series => series.name === data.name)) {
13-
updatedOptions.series.push(data);
14-
}
15-
}
16-
17-
return updatedOptions;
18-
}
19-
20-
export function removeSeries(
21-
currentOptions: ChartOptions,
22-
name: string,
23-
): Partial<ChartOptions> {
24-
const updatedOptions = { ...currentOptions };
25-
26-
if (updatedOptions.series) {
27-
updatedOptions.series = [
28-
...updatedOptions.series.filter(series => series.name !== name),
29-
];
30-
}
31-
32-
return updatedOptions;
33-
}
34-
35-
/**
36-
* Method to recursively merge two objects. It should update keys if they
37-
* already exist and add them if they don't. However, it shouldn't completely
38-
* overwrite a key it's an already existing object.
39-
*
40-
* They goal is to allow for partial updates to the chart options object.
41-
*/
42-
function recursiveMerge(
43-
target: { [key: string]: any },
44-
source: { [key: string]: any },
45-
) {
46-
const updatedObj = { ...target };
47-
48-
for (const key in source) {
49-
if (
50-
typeof source[key] === 'object' &&
51-
typeof updatedObj[key] === 'object'
52-
) {
53-
// Recursively update nested objects
54-
updatedObj[key] = recursiveMerge(updatedObj[key], source[key]);
55-
} else {
56-
// Update or add the value for the key
57-
updatedObj[key] = source[key];
58-
}
9+
const prevSeries =
10+
currentOptions && Array.isArray(currentOptions?.series)
11+
? currentOptions.series
12+
: [];
13+
14+
const hasSeriesData = prevSeries.some(
15+
series => series.id && series.id === data.id,
16+
);
17+
18+
if (hasSeriesData) {
19+
return {
20+
series: prevSeries,
21+
};
5922
}
6023

61-
return updatedObj;
24+
return {
25+
series: [...prevSeries, data],
26+
};
6227
}
6328

64-
export function updateOptions(
65-
currentOptions: ChartOptions,
66-
options: Partial<ChartOptions>,
29+
export function getOptionsToUpdateWithRemovedSeries(
30+
currentOptions: ECBasicOption | undefined,
31+
id: string,
6732
): Partial<ChartOptions> {
68-
return recursiveMerge(currentOptions, options);
33+
const prevSeries =
34+
currentOptions &&
35+
currentOptions.series &&
36+
Array.isArray(currentOptions?.series)
37+
? currentOptions.series
38+
: [];
39+
40+
const filteredSeries = prevSeries.filter(series => {
41+
if (!series) return true;
42+
43+
return series.id && series.id !== id;
44+
});
45+
46+
return {
47+
series: filteredSeries,
48+
};
6949
}

0 commit comments

Comments
 (0)