Skip to content

Commit 10e23bf

Browse files
visikyYiSiWang14
authored
feat: violin (#2593)
* feat(violin): 添加小提琴图 (#2569) * feat(violin): 添加小提琴图 * fix(violin): demo 的数据放到 CDN * doc(violin): 更新截图 * fix(violin): x 轴字段用常量定义 * refactor(violin): 去除 simple-statistics 包依赖,减小体积 * fix(violin): 修复不响应用户 tooltip 选项的问题 * fix(violin): 修复小提琴图中箱线图四分位的计算问题 * fix(violin): smooth 和 hollow API 改为 shape, 重命名字段常量 * docs(violin): 优化 box.textMap 的文档和 Demo * feat(violin): 使用 geometry 通用 adaptor, 添加格式化支持 * fix(violin): 修复 shape 通道未映射的问题 Co-authored-by: 14 <[email protected]> * fix(violin): 修复小提琴图轴重复 * fix(violin): 修复小提琴图 axis, legend 问题 * fix(violin): 修复小提琴图单测报错 * test(violin): 增加小提琴图 legend & axis 单测 * refactor(violin): 优化小提琴图 tooltip & 移除无用 series 字段 * chore: 增大包限制到 910kb * test(violin): 修复单测问题 & 增加单测覆盖 * feat(violin): 修改小提琴图动画和图形标注 * test(violin): 增加小提琴图 meta 单测 & 完善文档 * test(violin): 增加单测 * refactor(violin): 删除无用的配置项 * fix(violin): 暂时修复下小提琴图的 legend 问题 * docs: 优化组件文档,小提琴图没有 slider * chore: 升级 surge-preivew * docs: 小提琴图暂时不支持 label * fix: 修复 violin 单测错误 * feat(violin): 小提琴图增加 box 配置 Co-authored-by: 14 <[email protected]> Co-authored-by: 14 <[email protected]>
1 parent afbca08 commit 10e23bf

38 files changed

+2626
-44
lines changed

.github/workflows/auto-inspection.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
- uses: actions/checkout@v2
1313
- name: Get branch name (pull request)
1414
run: echo "BRANCH_NAME=$(echo ${GITHUB_HEAD_REF})" >> $GITHUB_ENV
15-
- uses: lxfu1/[email protected]
15+
- uses: lxfu1/[email protected]
1616
id: preview_step
1717
with:
1818
project_name: 'G2Plot'

__tests__/data/violin.ts

+602
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { Violin } from '../../../../src';
2+
import { BASE_VIOLIN_DATA } from '../../../data/violin';
3+
import { createDiv } from '../../../utils/dom';
4+
5+
describe('violin legend', () => {
6+
const violin = new Violin(createDiv(), {
7+
width: 400,
8+
height: 500,
9+
data: BASE_VIOLIN_DATA,
10+
xField: 'type',
11+
yField: 'value',
12+
animation: {
13+
enter: {
14+
animation: 'fade-in',
15+
},
16+
leave: {
17+
animation: 'fade-out',
18+
},
19+
},
20+
});
21+
22+
violin.render();
23+
24+
it('default', () => {
25+
// 新的 geometry violin 暂时未追加
26+
expect(violin.chart.views[0].geometries[0].animateOption).toEqual({
27+
enter: {
28+
animation: 'fade-in',
29+
},
30+
leave: {
31+
animation: 'fade-out',
32+
},
33+
});
34+
35+
// 追加默认的动画配置
36+
expect(violin.chart.views[1].geometries[0].animateOption).toMatchObject({
37+
appear: {
38+
duration: 450,
39+
easing: 'easeQuadOut',
40+
},
41+
update: {
42+
duration: 400,
43+
easing: 'easeQuadInOut',
44+
},
45+
enter: {
46+
duration: 400,
47+
easing: 'easeQuadInOut',
48+
animation: 'fade-in',
49+
},
50+
leave: {
51+
duration: 350,
52+
easing: 'easeQuadIn',
53+
animation: 'fade-out',
54+
},
55+
});
56+
});
57+
58+
it('update', () => {
59+
violin.update({
60+
animation: {
61+
appear: {
62+
animation: 'fade-in',
63+
},
64+
leave: {
65+
animation: 'wave-out',
66+
},
67+
},
68+
});
69+
expect(violin.chart.views[0].geometries[0].animateOption).toEqual({
70+
appear: {
71+
animation: 'fade-in',
72+
},
73+
enter: {
74+
animation: 'fade-in',
75+
},
76+
leave: {
77+
animation: 'wave-out',
78+
},
79+
});
80+
});
81+
82+
afterAll(() => {
83+
violin.destroy();
84+
});
85+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { Violin } from '../../../../src';
2+
import { BASE_VIOLIN_DATA } from '../../../data/violin';
3+
import { createDiv } from '../../../utils/dom';
4+
5+
describe('violin legend', () => {
6+
const violin = new Violin(createDiv(), {
7+
width: 400,
8+
height: 500,
9+
data: BASE_VIOLIN_DATA,
10+
xField: 'type',
11+
yField: 'value',
12+
});
13+
14+
violin.render();
15+
16+
it('text annotation', () => {
17+
violin.update({
18+
annotations: [
19+
{
20+
type: 'text',
21+
position: ['median', 'median'],
22+
content: '辅助文本',
23+
},
24+
],
25+
});
26+
27+
const controller = violin.chart.views[0].getController('annotation');
28+
expect(controller.getComponents().length).toBe(1);
29+
expect(controller.getComponents()[0].component.get('content')).toBe('辅助文本');
30+
});
31+
32+
it('text annotation and line annotation', () => {
33+
violin.update({
34+
...violin.options,
35+
annotations: [
36+
{
37+
type: 'text',
38+
position: ['min', 'median'],
39+
content: '辅助文本',
40+
offsetY: -4,
41+
style: {
42+
textBaseviolin: 'bottom',
43+
},
44+
},
45+
{
46+
type: 'line',
47+
start: ['min', 'median'],
48+
end: ['max', 'median'],
49+
style: {
50+
stroke: 'red',
51+
violinDash: [2, 2],
52+
},
53+
},
54+
],
55+
});
56+
const controller = violin.chart.views[0].getController('annotation');
57+
expect(controller.getComponents().length).toBe(2);
58+
expect(controller.getComponents()[0].component.get('content')).toBe('辅助文本');
59+
expect(controller.getComponents()[1].component.get('type')).toBe('line');
60+
});
61+
62+
afterAll(() => {
63+
violin.destroy();
64+
});
65+
});
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { Violin } from '../../../../src';
2+
import { BASE_VIOLIN_DATA } from '../../../data/violin';
3+
import { createDiv } from '../../../utils/dom';
4+
5+
describe('violin axis', () => {
6+
it('没有 seriesField', () => {
7+
const violin = new Violin(createDiv(), {
8+
width: 400,
9+
height: 500,
10+
data: BASE_VIOLIN_DATA,
11+
xField: 'type',
12+
yField: 'value',
13+
});
14+
15+
violin.render();
16+
17+
let axisController = violin.chart.views[0].getController('axis');
18+
expect(axisController.getComponents().length).toBe(4);
19+
20+
violin.update({ xAxis: { grid: null } });
21+
axisController = violin.chart.views[0].getController('axis');
22+
expect(axisController.getComponents().length).toBe(3);
23+
24+
violin.update({ xAxis: { title: { text: 'xx' } } });
25+
axisController = violin.chart.views[0].getController('axis');
26+
expect(axisController.getComponents()[0].component.get('title').text).toBe('xx');
27+
28+
// 关闭 xAxis
29+
violin.update({ xAxis: false });
30+
axisController = violin.chart.views[0].getController('axis');
31+
expect(axisController.getComponents().length).toBe(2);
32+
33+
violin.destroy();
34+
});
35+
36+
it('有 seriesField', () => {
37+
const violin = new Violin(createDiv(), {
38+
width: 400,
39+
height: 500,
40+
data: BASE_VIOLIN_DATA,
41+
xField: 'type',
42+
yField: 'value',
43+
seriesField: 'species',
44+
});
45+
46+
violin.render();
47+
let axisController = violin.chart.views[0].getController('axis');
48+
expect(axisController.getComponents().length).toBe(4);
49+
50+
violin.update({ xAxis: { grid: null } });
51+
axisController = violin.chart.views[0].getController('axis');
52+
expect(axisController.getComponents().length).toBe(3);
53+
54+
violin.update({ xAxis: { title: { text: 'xx' } } });
55+
axisController = violin.chart.views[0].getController('axis');
56+
expect(axisController.getComponents()[0].component.get('title').text).toBe('xx');
57+
58+
// 关闭 xAxis
59+
violin.update({ xAxis: false });
60+
axisController = violin.chart.views[0].getController('axis');
61+
expect(axisController.getComponents().length).toBe(2);
62+
63+
violin.destroy();
64+
});
65+
});
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Violin } from '../../../../src';
2+
import { MIN_MAX_VIEW_ID, QUANTILE_VIEW_ID, MEDIAN_VIEW_ID } from '../../../../src/plots/violin/constant';
3+
import { BASE_VIOLIN_DATA } from '../../../data/violin';
4+
import { createDiv } from '../../../utils/dom';
5+
6+
describe('violin', () => {
7+
it('renders box views.', () => {
8+
const violin = new Violin(createDiv(), {
9+
width: 400,
10+
height: 500,
11+
data: BASE_VIOLIN_DATA,
12+
xField: 'type',
13+
yField: 'value',
14+
});
15+
16+
violin.render();
17+
const minMaxView = violin.chart.views.find((view) => view.id === MIN_MAX_VIEW_ID);
18+
const quantileView = violin.chart.views.find((view) => view.id === QUANTILE_VIEW_ID);
19+
const medianView = violin.chart.views.find((view) => view.id === MEDIAN_VIEW_ID);
20+
21+
expect(minMaxView.geometries[0].type).toBe('interval');
22+
expect(quantileView.geometries[0].type).toBe('interval');
23+
expect(medianView.geometries[0].type).toBe('point');
24+
25+
violin.destroy();
26+
});
27+
28+
// 暂时不开放 box 配置
29+
it("should not render box views when 'box' set to false.", () => {
30+
const violin = new Violin(createDiv(), {
31+
width: 400,
32+
height: 500,
33+
data: BASE_VIOLIN_DATA,
34+
xField: 'type',
35+
yField: 'value',
36+
box: false,
37+
});
38+
39+
violin.render();
40+
const minMaxView = violin.chart.views.find((view) => view.id === MIN_MAX_VIEW_ID);
41+
const quantileView = violin.chart.views.find((view) => view.id === QUANTILE_VIEW_ID);
42+
const medianView = violin.chart.views.find((view) => view.id === MEDIAN_VIEW_ID);
43+
44+
expect(minMaxView).toBeUndefined();
45+
expect(quantileView).toBeUndefined();
46+
expect(medianView).toBeUndefined();
47+
48+
violin.destroy();
49+
});
50+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { group } from '@antv/util';
2+
import { Violin } from '../../../../src';
3+
import { VIOLIN_VIEW_ID } from '../../../../src/plots/violin/constant';
4+
import { BASE_VIOLIN_DATA } from '../../../data/violin';
5+
import { createDiv } from '../../../utils/dom';
6+
7+
describe('violin change data', () => {
8+
it('renders new violins when data changed', () => {
9+
const violin = new Violin(createDiv(), {
10+
width: 400,
11+
height: 500,
12+
data: BASE_VIOLIN_DATA,
13+
xField: 'type',
14+
yField: 'value',
15+
});
16+
17+
violin.render();
18+
const g = violin.chart.views.find((view) => view.id === VIOLIN_VIEW_ID).geometries[0];
19+
expect(g.elements.length).toBe(group(BASE_VIOLIN_DATA, 'type').length);
20+
21+
const newData = BASE_VIOLIN_DATA.filter((data) => data.type !== 'PetalWidth');
22+
23+
violin.changeData(newData);
24+
const newG = violin.chart.views.find((view) => view.id === VIOLIN_VIEW_ID).geometries[0];
25+
expect(violin.options.data).toEqual(newData);
26+
expect(newG.elements.length).toBe(group(newData, 'type').length);
27+
28+
violin.destroy();
29+
});
30+
});

0 commit comments

Comments
 (0)