Skip to content

Commit f9b9acf

Browse files
visikyxinming
and
xinming
authored
feat(pie-annotation): add pie options (#1301)
* feat(pie-annotation): add pie options 1. add annotations 2. add quick annotation: statistic in the center 3. add statistic interaction * feat(v2/pie-annotations): annotations 更新时,需要先clear * feat(v2/pie-annotations): update statistic config & add testcases 1. 修改 statistic formatter 参数, 第一个参数是指标统计数据,第二是原始数据 2. 增加 interactions 测试用例 * feat(v2/pie-annotations): 增加 utils 测试单例 * refactor(v2/pie): 修改获取fields方式 & 其他 * fix(v2/pie): 修改 cr 建议 * fix(v2/pie): 修改饼图测试用例 Co-authored-by: xinming <[email protected]>
1 parent cd3be27 commit f9b9acf

File tree

11 files changed

+764
-10
lines changed

11 files changed

+764
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { getInteraction, getActionClass } from '@antv/g2';
2+
import InteractionContext from '@antv/g2/lib/interaction/context';
3+
import { delay } from '../../../utils/delay';
4+
import { createDiv } from '../../../utils/dom';
5+
import { Pie } from '../../../../src';
6+
import { StatisticAction } from '../../../../src/plots/pie/interaction';
7+
8+
describe('register interaction', () => {
9+
it('创建 "pie-statistic" action', () => {
10+
const action = getActionClass('pie-statistic');
11+
expect(action).toBe(StatisticAction);
12+
expect(action.name).toBe('StatisticAction');
13+
});
14+
15+
it('注册 "pie-statistic-active" 交互', () => {
16+
const statisticInteraction = getInteraction('pie-statistic-active');
17+
expect(statisticInteraction).toBeDefined();
18+
});
19+
20+
const pie = new Pie(createDiv(), {
21+
width: 400,
22+
height: 300,
23+
data: [
24+
{ type: 'item1', value: 10 },
25+
{ type: 'item2', value: 13 },
26+
],
27+
angleField: 'value',
28+
colorField: 'type',
29+
radius: 0.8,
30+
innerRadius: 0.64,
31+
statistic: {
32+
title: { formatter: (item, data) => (!Array.isArray(data) ? item.title : 'Total') },
33+
},
34+
});
35+
36+
pie.render();
37+
38+
const context = new InteractionContext(pie.chart);
39+
const action = new StatisticAction(context);
40+
41+
it('触发 pie-statistic:change', async () => {
42+
context.event = { data: { data: { type: 'item3', value: 13 } } };
43+
action.change();
44+
45+
delay(5000);
46+
const annotations = context.view.getComponents().filter((co) => co.type === 'annotation');
47+
expect(annotations[0].extra.content).toBe('item3');
48+
expect(annotations[1].extra.content).toBe(13);
49+
});
50+
51+
it('触发 pie-statistic:reset', async () => {
52+
action.reset();
53+
54+
delay(5000);
55+
const annotations = context.view.getComponents().filter((co) => co.type === 'annotation');
56+
expect(annotations[0].extra.content).toBe('Total');
57+
});
58+
});
+326
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
import { Chart } from '@antv/g2';
2+
import { Pie, PieOptions } from '../../../../src';
3+
import { POSITIVE_NEGATIVE_DATA } from '../../../data/common';
4+
import { delay } from '../../../utils/delay';
5+
import { createDiv } from '../../../utils/dom';
6+
7+
describe('中心文本 - 指标卡', () => {
8+
const data = POSITIVE_NEGATIVE_DATA.filter((o) => o.value > 0).map((d, idx) =>
9+
idx === 1 ? { ...d, type: 'item1' } : d
10+
);
11+
12+
const config: PieOptions = {
13+
width: 400,
14+
height: 300,
15+
data,
16+
angleField: 'value',
17+
colorField: 'type',
18+
radius: 0.8,
19+
statistic: {},
20+
};
21+
const pie = new Pie(createDiv(), config);
22+
pie.render();
23+
24+
function getAnnotations(chart: Chart) {
25+
return chart.getComponents().filter((co) => co.type === 'annotation');
26+
}
27+
28+
it('没有设置 inner radius,不展示中心文本指标卡', () => {
29+
expect(getAnnotations(pie.chart).length).toBe(0);
30+
});
31+
32+
it('设置 inner radius', () => {
33+
pie.update({
34+
...pie.options,
35+
innerRadius: 0.64,
36+
});
37+
pie.render();
38+
39+
const annotations = getAnnotations(pie.chart);
40+
expect(annotations.length).toBeGreaterThan(0);
41+
expect(annotations[0].component.get('content')).toBe('总计' /** 中心文本指标卡,默认title */);
42+
});
43+
44+
it('自定义中心文本内容: title & content', () => {
45+
const pie = new Pie(createDiv(), {
46+
...config,
47+
innerRadius: 0.64,
48+
statistic: {
49+
title: {
50+
formatter: () => '总计',
51+
},
52+
content: {
53+
formatter: () => 'test\ntest',
54+
rotate: (30 / 180) * Math.PI,
55+
},
56+
},
57+
});
58+
59+
pie.render();
60+
61+
const annotations = getAnnotations(pie.chart);
62+
expect(annotations.length).toBeGreaterThan(0);
63+
expect(annotations[0].component.get('content')).toBe('总计');
64+
expect(annotations[1].component.get('content')).toBe('test\ntest');
65+
});
66+
67+
it('自定义中心文本内容: update statistic title & content', () => {
68+
pie.update({
69+
...pie.options,
70+
statistic: {
71+
title: {
72+
formatter: () => '总计',
73+
},
74+
content: {
75+
formatter: () => 'test\ntest',
76+
rotate: (30 / 180) * Math.PI,
77+
},
78+
},
79+
});
80+
81+
pie.render();
82+
83+
const annotations = getAnnotations(pie.chart);
84+
expect(annotations.length).toBeGreaterThan(0);
85+
expect(annotations[0].component.get('content')).toBe('总计');
86+
expect(annotations[1].component.get('content')).toBe('test\ntest');
87+
});
88+
89+
it('自定义中心文本内容: title & content, 动态数据', () => {
90+
const pie = new Pie(createDiv(), {
91+
...config,
92+
innerRadius: 0.64,
93+
statistic: {
94+
title: {
95+
formatter: (item, data) => (Array.isArray(data) ? '总计' : data['type']),
96+
},
97+
content: {
98+
formatter: (item, data) => {
99+
return Array.isArray(data) ? 'test\ntest' : typeof data.value === 'number' ? `${data.value}` : '';
100+
},
101+
rotate: (30 / 180) * Math.PI,
102+
},
103+
},
104+
});
105+
106+
pie.render();
107+
108+
const annotations = getAnnotations(pie.chart);
109+
expect(annotations.length).toBeGreaterThan(0);
110+
expect(annotations[0].component.get('content')).toBe('总计');
111+
expect(annotations[1].component.get('content')).toBe('test\ntest');
112+
});
113+
114+
it('自定义中心文本内容: update statistic title & content, 动态数据', async () => {
115+
await delay(5000);
116+
pie.update({
117+
...pie.options,
118+
statistic: {
119+
title: {
120+
formatter: (item, data) => (Array.isArray(data) ? '总计' : data['type']),
121+
},
122+
content: {
123+
formatter: (item, data) => {
124+
return Array.isArray(data) ? 'test\ntest' : typeof data.value === 'number' ? `${data.value}` : '';
125+
},
126+
rotate: (30 / 180) * Math.PI,
127+
},
128+
},
129+
});
130+
131+
pie.render();
132+
133+
const annotations = getAnnotations(pie.chart);
134+
expect(annotations.length).toBeGreaterThan(0);
135+
expect(annotations[0].component.get('content')).toBe('总计');
136+
expect(annotations[1].component.get('content')).toBe('test\ntest');
137+
});
138+
139+
it('自定义中心文本样式: title style & content style', () => {
140+
const pie = new Pie(createDiv(), {
141+
...config,
142+
innerRadius: 0.64,
143+
statistic: {
144+
title: {
145+
formatter: () => '',
146+
style: { fill: 'red' },
147+
},
148+
content: {
149+
style: { fill: 'pink' },
150+
},
151+
},
152+
});
153+
154+
pie.render();
155+
const annotations = getAnnotations(pie.chart);
156+
expect(annotations.length).toBe(2);
157+
expect(annotations[0].extra.content).toBe('');
158+
expect(annotations[0].extra.key).toBe('statistic');
159+
expect(annotations[0].extra.style).toMatchObject({ fill: 'red' });
160+
expect(annotations[1].extra.style).toMatchObject({ fill: 'pink' });
161+
});
162+
163+
it('自定义中心文本样式: update statistic title style & content style', async () => {
164+
await delay(5000);
165+
pie.update({
166+
...pie.options,
167+
statistic: {
168+
title: {
169+
formatter: () => '',
170+
style: { fill: 'red' },
171+
},
172+
content: {
173+
style: { fill: 'pink' },
174+
},
175+
},
176+
});
177+
178+
pie.render();
179+
const annotations = getAnnotations(pie.chart);
180+
expect(annotations.length).toBe(2);
181+
expect(annotations[0].extra.content).toBe('');
182+
expect(annotations[0].extra.key).toBe('statistic');
183+
expect(annotations[0].extra.style).toMatchObject({ fill: 'red' });
184+
expect(annotations[1].extra.style).toMatchObject({ fill: 'pink' });
185+
});
186+
187+
// 暂时不提供 annotations 配置
188+
// it('append annotation', () => {
189+
// const pie = new Pie(createDiv(), {
190+
// ...config,
191+
// innerRadius: 0.64,
192+
// statistic: {
193+
// title: {
194+
// formatter: () => '',
195+
// },
196+
// content: {
197+
// formatter: () => '无数据',
198+
// },
199+
// },
200+
// annotations: [
201+
// {
202+
// type: 'text',
203+
// top: true,
204+
// position: ['50%', '20%'],
205+
// content: '达标区间',
206+
// style: {
207+
// fill: '#aaaaaa',
208+
// textAlign: 'end',
209+
// textBaseline: 'top',
210+
// fontWeight: 300,
211+
// },
212+
// offsetX: -10,
213+
// offsetY: 6,
214+
// },
215+
// ],
216+
// });
217+
218+
// pie.render();
219+
// const annotations = getAnnotations(pie.chart);
220+
// expect(annotations.length).toBe(3);
221+
// expect(annotations[0].component.get('type')).toBe('text');
222+
// expect(annotations[0].extra.content).toBe('达标区间');
223+
// expect(annotations[1].extra.content).toBe('');
224+
// });
225+
226+
// it('关闭 stastic,自定义 annotation', async () => {
227+
// const pie = new Pie(createDiv(), {
228+
// ...config,
229+
// innerRadius: 0.64,
230+
// statistic: null,
231+
// annotations: [
232+
// {
233+
// type: 'image',
234+
// position: ['50%', '50%'],
235+
// src: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
236+
// offsetX: -28,
237+
// offsetY: 30,
238+
// style: {
239+
// width: 56,
240+
// height: 56,
241+
// },
242+
// },
243+
// ],
244+
// });
245+
246+
// pie.render();
247+
248+
// const annotations = getAnnotations(pie.chart);
249+
// expect(annotations.length).toBe(1);
250+
// expect(annotations[0].component.get('type')).toBe('image');
251+
252+
// await delay(5000);
253+
// pie.update({
254+
// ...pie.options,
255+
// annotations: [
256+
// {
257+
// type: 'text',
258+
// position: ['50%', '50%'],
259+
// content: '自定义标注文本',
260+
// style: {
261+
// textAlign: 'center',
262+
// },
263+
// },
264+
// ],
265+
// });
266+
// pie.render();
267+
268+
// const newAnnotations = getAnnotations(pie.chart);
269+
// expect(newAnnotations.length).toBe(1);
270+
// expect(newAnnotations[0].component.get('type')).toBe('text');
271+
// expect(newAnnotations[0].component.get('content')).toBe('自定义标注文本');
272+
// });
273+
274+
it('总计值为空', () => {
275+
const pie = new Pie(createDiv(), {
276+
...config,
277+
data: config.data.map((d) => ({ ...d, value: null })),
278+
innerRadius: 0.64,
279+
});
280+
pie.render();
281+
282+
const annotations = getAnnotations(pie.chart);
283+
expect(annotations.length).toBe(2);
284+
expect(annotations[1].component.get('content')).toBe(null);
285+
});
286+
287+
it('添加交互 interaction', () => {
288+
const pie = new Pie(createDiv(), {
289+
...config,
290+
innerRadius: 0.64,
291+
interactions: [
292+
{ name: 'pie-statistic-active' },
293+
{ name: 'legend-filter', cfg: { start: [{ trigger: 'none' /** 通过此方式可以关闭交互 */ }] } },
294+
],
295+
});
296+
297+
pie.render();
298+
299+
const interactions = pie.chart.interactions;
300+
expect(interactions['pie-statistic-active']).toBeDefined();
301+
const legendInteraction = interactions['legend-filter'];
302+
expect(legendInteraction).toBeDefined();
303+
// @ts-ignore
304+
expect(legendInteraction.steps['start'][0].trigger).toEqual('none');
305+
});
306+
307+
it('自定义 pie-statistic-active 交互的 trigger 事件', () => {
308+
const pie = new Pie(createDiv(), {
309+
...config,
310+
innerRadius: 0.64,
311+
interactions: [
312+
{
313+
name: 'pie-statistic-active',
314+
cfg: { start: [{ trigger: 'element:click', action: 'pie-statistic:change' }] },
315+
},
316+
],
317+
});
318+
319+
pie.render();
320+
321+
const interactions = pie.chart.interactions;
322+
const statisticInteraction = interactions['pie-statistic-active'];
323+
// @ts-ignore
324+
expect(statisticInteraction.steps['start'][0].trigger).toEqual('element:click');
325+
});
326+
});

0 commit comments

Comments
 (0)