Skip to content

Commit e6c2d24

Browse files
committed
fix: fix Scatter regression line and Dual Axes change data
1 parent 63412f0 commit e6c2d24

File tree

7 files changed

+183
-48
lines changed

7 files changed

+183
-48
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { DualAxes } from '../../../../src';
2+
import { PV_DATA, UV_DATA, PV_DATA_MULTI, UV_DATA_MULTI } from '../../../data/pv-uv';
3+
import {} from '../../../data/pv-uv';
4+
import { createDiv } from '../../../utils/dom';
5+
import { LEFT_AXES_VIEW, RIGHT_AXES_VIEW } from '../../../../src/plots/dual-axes/constant';
6+
import { findViewById } from '../../../../src/utils/view';
7+
8+
describe('Line-Column', () => {
9+
it('Line-Colomn', () => {
10+
const dualAxes = new DualAxes(createDiv(), {
11+
height: 500,
12+
data: [[], []],
13+
xField: 'date',
14+
yField: ['pv', 'uv'],
15+
geometryOptions: [
16+
{
17+
geometry: 'line',
18+
connectNulls: false,
19+
smooth: true,
20+
color: '#f00',
21+
},
22+
{
23+
geometry: 'column',
24+
},
25+
],
26+
});
27+
28+
dualAxes.render();
29+
// 先柱后线
30+
expect(dualAxes.chart.views[0].id).toBe(RIGHT_AXES_VIEW);
31+
expect(dualAxes.chart.views[1].id).toBe(LEFT_AXES_VIEW);
32+
dualAxes.changeData([PV_DATA, UV_DATA]);
33+
const leftView = findViewById(dualAxes.chart, LEFT_AXES_VIEW);
34+
const rightView = findViewById(dualAxes.chart, RIGHT_AXES_VIEW);
35+
// line
36+
const leftGeometry = leftView.geometries.find((g) => g.type === 'line');
37+
const rightGeometry = rightView.geometries.find((g) => g.type === 'interval');
38+
// @ts-ignore
39+
expect(leftGeometry.shapeType).toBe('line');
40+
expect(rightGeometry.shapeType).toBe('interval');
41+
dualAxes.destroy();
42+
});
43+
44+
it('stack column and mutilpe line', () => {
45+
const dualAxes = new DualAxes(createDiv('test DualAxes change data'), {
46+
width: 400,
47+
height: 500,
48+
data: [[], []],
49+
xField: 'date',
50+
yField: ['pv', 'uv'],
51+
geometryOptions: [
52+
{
53+
geometry: 'column',
54+
seriesField: 'site',
55+
isStack: true,
56+
},
57+
{
58+
geometry: 'line',
59+
seriesField: 'site',
60+
isStack: true,
61+
point: {
62+
style: () => ({ fill: 'red' }),
63+
},
64+
},
65+
],
66+
});
67+
68+
dualAxes.render();
69+
expect(dualAxes.chart.getOptions().data.length).toBe(0);
70+
dualAxes.changeData([PV_DATA_MULTI, UV_DATA_MULTI]);
71+
const element = dualAxes.chart.views[1].geometries[1].elements[0];
72+
expect(element.getModel().shape).toBe('circle');
73+
expect(element.getData().site).toBeDefined();
74+
expect(element.getModel().style.fill).toBe('red');
75+
});
76+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Histogram } from '../../../../src';
2+
import { histogramData } from '../../../data/histogram-data';
3+
import { createDiv } from '../../../utils/dom';
4+
5+
describe('Histogram: change data', () => {
6+
const histogram = new Histogram(createDiv(), {
7+
width: 400,
8+
height: 300,
9+
appendPadding: 10,
10+
data: [],
11+
binField: 'value',
12+
binWidth: 2,
13+
tooltip: {
14+
title: 'hello wold!',
15+
},
16+
});
17+
18+
histogram.render();
19+
20+
it('change data', () => {
21+
// @ts-ignore
22+
expect(histogram.chart.options.tooltip.title).toBe('hello wold!');
23+
histogram.changeData(histogramData);
24+
expect(histogram.chart.getData().length).toBe(12);
25+
});
26+
});

__tests__/unit/plots/scatter/legend-spec.ts

+57
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,61 @@ describe('scatter', () => {
8080
expect(legendController.getComponents()[0].id).toBe('legend-gender');
8181
expect(legendController.getComponents()[0].direction).toBe('top-right');
8282
});
83+
84+
it('legend: legend * sizeField * false', () => {
85+
const scatter = new Scatter(createDiv(), {
86+
width: 400,
87+
height: 300,
88+
appendPadding: 10,
89+
data,
90+
xField: 'weight',
91+
yField: 'height',
92+
shapeField: 'gender',
93+
size: [2, 8],
94+
sizeField: 'weight',
95+
color: ['red', 'blue'],
96+
colorField: 'gender',
97+
xAxis: {
98+
nice: true,
99+
},
100+
legend: false,
101+
});
102+
103+
scatter.render();
104+
const legendController = scatter.chart.getController('legend');
105+
// @ts-ignore
106+
const { option } = legendController;
107+
expect(option).toBe(false);
108+
});
109+
110+
it('legend: legend * sizeField * true', () => {
111+
const scatter = new Scatter(createDiv(), {
112+
width: 400,
113+
height: 300,
114+
appendPadding: 10,
115+
data,
116+
xField: 'weight',
117+
yField: 'height',
118+
shapeField: 'gender',
119+
size: [2, 8],
120+
sizeField: 'weight',
121+
color: ['red', 'blue'],
122+
colorField: 'gender',
123+
xAxis: {
124+
nice: true,
125+
},
126+
legend: {},
127+
});
128+
129+
scatter.render();
130+
const legendController = scatter.chart.getController('legend');
131+
// @ts-ignore
132+
const {
133+
// @ts-ignore
134+
option: { weight, height, gender },
135+
} = legendController;
136+
expect(weight).toBe(false);
137+
expect(height).toBe(false);
138+
expect(gender).toBeTruthy();
139+
});
83140
});

__tests__/unit/plots/scatter/regression-line-spec.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Scatter } from '../../../../src';
22
import { createDiv } from '../../../utils/dom';
3+
import { delay } from '../../../utils/delay';
34

45
const data = [
56
{ x: 1, y: 4.181 },
@@ -72,7 +73,7 @@ describe('scatter', () => {
7273
expect(elements[0].getModel().size).toBe(5);
7374
});
7475

75-
it('regressionLine: algorithm', () => {
76+
it('regressionLine: algorithm', async () => {
7677
const scatter = new Scatter(createDiv('regressionLine*algorithm'), {
7778
data,
7879
width: 400,
@@ -86,16 +87,18 @@ describe('scatter', () => {
8687
lineWidth: 1,
8788
fill: '#5B8FF9',
8889
},
90+
animation: false,
8991
regressionLine: {
9092
algorithm: [
91-
[0, 0],
92-
[200, 200],
93+
[8, 6],
94+
[16, 7],
95+
[24, 7],
9396
],
9497
},
9598
});
9699

97100
scatter.render();
98-
101+
await delay(500);
99102
const geometry = scatter.chart.geometries[0];
100103
const annotation = scatter.chart.annotation();
101104
// @ts-ignore
@@ -106,5 +109,13 @@ describe('scatter', () => {
106109
// @ts-ignore
107110
expect(elements[0].getModel().style.fill).toBe('#5B8FF9');
108111
expect(elements[0].getModel().size).toBe(5);
112+
const { width } = scatter.chart;
113+
const pathGroup = scatter.chart
114+
.getComponents()
115+
.find((item) => item.type === 'annotation')
116+
.component.cfg.group.cfg.children[0].getChildren();
117+
const { path } = pathGroup?.[0]?.cfg?.attrs;
118+
expect(path.length).toBe(3);
119+
expect(scatter.chart.getXScale().scale(8) * width < path[0][1]).toBeTruthy();
109120
});
110121
});

src/plots/dual-axes/index.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ export class DualAxes extends Plot<DualAxesOptions> {
2020
geometryOptions,
2121
({ geometry }) => geometry === DualAxesGeometry.Line || geometry === undefined
2222
);
23-
2423
return deepAssign({}, super.getDefaultOptions(options), {
2524
yAxis: [],
26-
geometryOptions: [],
25+
geometryOptions,
2726
meta: {
2827
[xField]: {
2928
// x 轴一定是同步 scale 的

src/plots/scatter/adaptor.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,14 @@ function legend(params: Params<ScatterOptions>): Params<ScatterOptions> {
8383

8484
if (legend) {
8585
chart.legend(colorField || shapeField, legend);
86+
// 隐藏连续图例
87+
if (sizeField) {
88+
chart.legend(sizeField, false);
89+
}
8690
} else {
8791
chart.legend(false);
8892
}
8993

90-
// 隐藏连续图例
91-
if (sizeField) {
92-
chart.legend(sizeField, false);
93-
}
94-
9594
return params;
9695
}
9796

src/plots/scatter/annotations/path.ts

+4-37
Original file line numberDiff line numberDiff line change
@@ -36,50 +36,17 @@ type path = {
3636
y: number;
3737
};
3838

39-
// 处理用户自行配置 min max的情况
40-
function adjustScale(viewScale: Scale, pathData: path[], dim: string, config: renderOptions) {
41-
const { min, max } = viewScale;
42-
const {
43-
options: { data, xField, yField },
44-
} = config;
45-
const field = dim === 'x' ? xField : yField;
46-
const dataMin = minBy(data, field)[field];
47-
const dataMax = maxBy(data, field)[field];
48-
const minRatio = (min - dataMin) / (dataMax - dataMin);
49-
const maxRatio = (max - dataMax) / (dataMax - dataMin);
50-
const trendMin = minBy(pathData, dim)[dim];
51-
const trendMax = maxBy(pathData, dim)[dim];
52-
return {
53-
min: trendMin + minRatio * (trendMax - trendMin),
54-
max: trendMax + maxRatio * (trendMax - trendMin),
55-
};
56-
}
57-
5839
function getPath(data: number[][], config: renderOptions) {
5940
const {
6041
view,
6142
options: { xField, yField },
6243
} = config;
63-
const pathData = data.map((d: [number, number]) => ({ x: d[0], y: d[1] }));
6444
const xScaleView = view.getScaleByField(xField);
6545
const yScaleView = view.getScaleByField(yField);
66-
const coordinate = view.getCoordinate();
67-
const linearScale = getScale('linear');
68-
const xRange = adjustScale(xScaleView, pathData, 'x', config);
69-
const xScale = new linearScale({
70-
min: xRange.min,
71-
max: xRange.max,
72-
});
73-
const yRange = adjustScale(yScaleView, pathData, 'y', config);
74-
const yScale = new linearScale({
75-
min: yRange.min,
76-
max: yRange.max,
77-
});
78-
const points = pathData.map((d) => ({
79-
x: coordinate.start.x + coordinate['width'] * xScale.scale(d.x),
80-
y: coordinate.start.y - coordinate['height'] * yScale.scale(d.y),
81-
}));
82-
return getSplinePath(points, false);
46+
const pathData = data.map((d: [number, number]) =>
47+
view.getCoordinate().convert({ x: xScaleView.scale(d[0]), y: yScaleView.scale(d[1]) })
48+
);
49+
return getSplinePath(pathData, false);
8350
}
8451

8552
function renderPath(config: renderOptions) {

0 commit comments

Comments
 (0)