Skip to content

Commit

Permalink
feat(change-data): 饼图支持动态更新数据 & 其他若干处理,见 PR 描述 (#2257)
Browse files Browse the repository at this point in the history
* feat(pie): 饼图支持动态更新数据 & 过滤非法数据,如 NaN

* docs(demo): 增加一个动态更新的饼图 demo

* test(pie): 添加饼图动态更新数据 & 非法数据 NaN 输入的单测

* refactor(pie): 将饼图 defaultOptions 作为静态方法获取 & 饼图中心文本更新后仍然可以用默认样式

* test(pie): 添加饼图 utils& default-options 单测

* fix: 修改单测问题

* test: 添加带 changedata 的 annotaton 单测

* test: 补充 defaultOptions 的单测

* test(pie): 补充单测
  • Loading branch information
visiky authored Jan 24, 2021
1 parent f1d44c3 commit 47fa687
Show file tree
Hide file tree
Showing 13 changed files with 415 additions and 88 deletions.
15 changes: 14 additions & 1 deletion __tests__/unit/core/index-spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { deepMix, isEqual, clone } from '@antv/util';
import { Line, G2 } from '../../../src';
import { Line, G2, Plot } from '../../../src';
import { partySupport } from '../../data/party-support';
import { createDiv } from '../../utils/dom';
import { delay } from '../../utils/delay';
Expand Down Expand Up @@ -300,4 +300,17 @@ describe('core', () => {

line.destroy();
});

it('default-options', () => {
type CustomPlotOptions = {};
class CustomPlot extends Plot<CustomPlotOptions> {
type: 'custom';
getSchemaAdaptor() {
return () => {};
}
}
const plot = new CustomPlot(createDiv(), {});
// @ts-ignore
expect(Plot.getDefaultOptions()).toEqual(plot.getDefaultOptions());
});
});
54 changes: 52 additions & 2 deletions __tests__/unit/plots/pie/annotation-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ describe('annotation', () => {

it('text annotation', () => {
expect(pie.chart.getController('annotation').getComponents().length).toBe(2);
const annotation1: HTMLDivElement = pie.chart.ele.querySelector('.g2-html-annotation');
// @ts-ignore
expect(annotation1.style.fontWeight).toBe(`${Pie.getDefaultOptions().statistic.title.style.fontWeight}`);

pie.update({
...pie.options,
annotations: [
{
type: 'text',
Expand All @@ -34,7 +36,6 @@ describe('annotation', () => {

it('text annotation and line annotation', () => {
pie.update({
...pie.options,
statistic: null,
annotations: [
{
Expand All @@ -54,6 +55,55 @@ describe('annotation', () => {
expect(pie.chart.getController('annotation').getComponents()[1].component.get('type')).toBe('line');
});

it('annotation with change data', () => {
pie.update({ data: [], statistic: {} });
expect(pie.chart.getController('annotation').getComponents().length).toBe(4);
expect(pie.chart.getController('annotation').getComponents()[0].component.get('content')).toBe('辅助文本');
// @ts-ignore
let annotations = pie.chart.ele.querySelectorAll('.g2-html-annotation') as HTMLDivElement[];
expect(annotations[0].innerText).toBe('总计');
expect(annotations[1].innerText).toBe('');

pie.changeData(salesByArea);
setTimeout(() => {
expect(pie.chart.getController('annotation').getComponents().length).toBe(4);
expect(pie.chart.getController('annotation').getComponents()[0].component.get('content')).toBe('辅助文本');
// @ts-ignore
annotations = pie.chart.ele.querySelectorAll('.g2-html-annotation') as HTMLDivElement[];
expect(annotations[0].innerText).toBe('总计');
expect(annotations[1].innerText).toBe(salesByArea.reduce((a, b) => a + b.sales, 0));
}, 0);
});

it('先更新为 false,再更新出现, 且样式不变', () => {
const pie1 = new Pie(createDiv(), {
width: 300,
height: 400,
data: salesByArea,
colorField: 'sales',
angleField: 'area',
innerRadius: 0.64,
});

pie1.render();
let annotation1: HTMLDivElement = pie1.chart.ele.querySelector('.g2-html-annotation');
const style = annotation1.style;

pie1.update({ statistic: { title: false, content: false } });
expect(pie1.chart.getController('annotation').getComponents().length).toBe(0);

pie1.update({ statistic: { title: {}, content: {} } });
expect(pie1.chart.getController('annotation').getComponents().length).toBe(2);
annotation1 = pie1.chart.ele.querySelector('.g2-html-annotation');
// @ts-ignore
expect(annotation1.innerText).toBe('总计');
// 样式依然是默认样式
// @ts-ignore
expect(annotation1.style).toEqual(style);

pie1.destroy();
});

afterAll(() => {
pie.destroy();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Pie } from '../../../../src';
import { createDiv } from '../../../utils/dom';
import { delay } from '../../../utils/delay';

describe('饼图 数据全空', () => {
describe('饼图 异常数据', () => {
const data = [];
for (let i = 0; i < 5; i++) {
data.push({ type: `类型 ${i}`, value: 0 });
Expand Down Expand Up @@ -99,10 +99,7 @@ describe('饼图 数据全空', () => {
expect(elements.length).toBe(0);

await delay(500);
pie.update({
...pie.options,
data,
});
pie.changeData(data);

elements = pie.chart.geometries[0].elements;
expect(every(elements, (ele) => ele.getBBox().width > 0)).toBe(true);
Expand All @@ -126,3 +123,109 @@ describe('饼图 数据全空', () => {
pie.destroy();
});
});

describe('数据存在 NaN', () => {
const createPie = (data): Pie => {
const pie = new Pie(createDiv(), {
angleField: 'value',
colorField: 'type',
data,
});

pie.render();
return pie;
};

it('初始化存在 NaN', () => {
const pie = createPie([
{ type: '1', value: NaN },
{ type: '2', value: 10 },
]);
expect(pie.chart).toBeDefined();
expect(pie.chart.getData()).toEqual([{ type: '2', value: 10 }]);

pie.destroy();
});

it('从正常数据 change 到存在 NaN', () => {
const pie = createPie([{ type: '2', value: 10 }]);
pie.changeData([{ type: '1', value: NaN }]);
expect(pie.chart).toBeDefined();
expect(pie.options.data).toEqual([{ type: '1', value: NaN }]);
expect(pie.chart.getData()).toEqual([]);
pie.destroy();
});

it('从 [] change 到存在 NaN', () => {
const pie = createPie([]);
pie.changeData([{ type: '1', value: NaN }]);
expect(pie.chart).toBeDefined();
expect(pie.options.data).toEqual([{ type: '1', value: NaN }]);
expect(pie.chart.getData()).toEqual([]);
pie.destroy();
});

it('从存在数据 0 change 到存在 NaN', () => {
const pie = createPie([
{ type: '1', value: 0 },
{ type: '1', value: 0 },
]);
expect(pie.chart.getData().length).toBe(2);
pie.changeData([{ type: '1', value: NaN }]);
expect(pie.chart).toBeDefined();
expect(pie.options.data).toEqual([{ type: '1', value: NaN }]);
expect(pie.chart.getData()).toEqual([]);

pie.changeData([
{ type: '1', value: NaN },
{ type: '2', value: 0 },
]);
expect(pie.chart.getData()[0]).toMatchObject({ type: '2', value: 0 });
expect(pie.chart.geometries[0].elements.length).toEqual(1);
pie.destroy();
});
});

describe('环图 changeData', () => {
const createDonut = (data): Pie => {
const donut = new Pie(createDiv(), {
angleField: 'value',
colorField: 'type',
data,
innerRadius: 0.6,
annotations: [{ type: 'text', content: 'xx', position: ['min', 'max'] }],
});

donut.render();
return donut;
};

it('normal', () => {
const donut = createDonut([]);
expect(donut.chart.getController('annotation').getComponents().length).toBe(3);

donut.changeData([
{ type: '1', value: 1 },
{ type: '2', value: 3 },
]);
expect(donut.chart.getController('annotation').getComponents().length).toBe(3);

donut.destroy();
});

it('从数据全 0 到正常', () => {
const donut = createDonut([
{ type: '1', value: 0 },
{ type: '2', value: 0 },
]);
expect(donut.chart.getController('annotation').getComponents().length).toBe(3);

donut.changeData([
{ type: '1', value: 1 },
{ type: '2', value: 3 },
]);
expect(donut.chart.getController('annotation').getComponents().length).toBe(3);

donut.destroy();
});
});
8 changes: 8 additions & 0 deletions __tests__/unit/plots/pie/index-spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Pie } from '../../../../src';
import { DEFAULT_OPTIONS } from '../../../../src/plots/pie/contants';
import { POSITIVE_NEGATIVE_DATA } from '../../../data/common';
import { createDiv } from '../../../utils/dom';

Expand All @@ -19,6 +20,9 @@ describe('pie', () => {
});

pie.render();
expect(pie.type).toBe('pie');
// @ts-ignore
expect(pie.getDefaultOptions()).toBe(Pie.getDefaultOptions());

const geometry = pie.chart.geometries[0];
const elements = geometry.elements;
Expand Down Expand Up @@ -167,4 +171,8 @@ describe('pie', () => {

pie.destroy();
});

it('defaultOptions 保持从 constants 中获取', () => {
expect(Pie.getDefaultOptions()).toEqual(DEFAULT_OPTIONS);
});
});
17 changes: 17 additions & 0 deletions __tests__/unit/plots/pie/statistic-spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Chart } from '@antv/g2';
import { Pie, PieOptions } from '../../../../src';
import { DEFAULT_OPTIONS } from '../../../../src/plots/pie/contants';
import { POSITIVE_NEGATIVE_DATA } from '../../../data/common';
import { delay } from '../../../utils/delay';
import { createDiv } from '../../../utils/dom';
Expand Down Expand Up @@ -301,6 +302,22 @@ describe('中心文本 - 指标卡', () => {
expect(htmlAnnotations[0].getBoundingClientRect().width).toEqual(600);
});

it('statistic 默认继承 defaultOptions', () => {
pie.update({ statistic: null });
expect(pie.chart.ele.querySelectorAll('.g2-html-annotation').length).toBe(0);

pie.update({ statistic: {} });
expect(pie.chart.ele.querySelectorAll('.g2-html-annotation').length).toBe(2);
expect(pie.options.statistic).toEqual({});
setTimeout(() => {
// @ts-ignore
const annotations = pie.chart.ele.querySelectorAll('.g2-html-annotation') as HTMLDivElement[];
expect(annotations[0].style.fontSize).toEqual(DEFAULT_OPTIONS.statistic.title.fontSize);
expect(annotations[1].style.color).toEqual(DEFAULT_OPTIONS.statistic.content.color);
expect(annotations[1].style).toMatchObject(DEFAULT_OPTIONS.statistic.content);
}, 0);
});

afterEach(() => {
pie.chart.clear();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { adaptOffset, getTotalValue } from '../../../../../src/plots/pie/utils';
import { adaptOffset, getTotalValue, isAllZero, processIllegalData } from '../../../../src/plots/pie/utils';

describe('utils of pie plot', () => {
const data = [
Expand Down Expand Up @@ -58,4 +58,28 @@ describe('utils of pie plot', () => {
expect(adaptOffset('spider', '30%')).toBe('30%');
expect(adaptOffset('spider', NaN)).toBe(NaN);
});

it('过滤非法数据', () => {
const data = [{ type: '1', value: 0 }];
expect(processIllegalData(data, 'value')).toEqual(data);
data.push({ type: '2', value: 1 });
expect(processIllegalData(data, 'value')).toEqual(data);
data.push({ type: '3', value: null });
expect(processIllegalData(data, 'value')).toEqual(data);
data.push({ type: '4', value: undefined });
expect(processIllegalData(data, 'value')).toEqual(data.slice(0, 3));
data.push({ type: '5', value: NaN });
expect(processIllegalData(data, 'value')).toEqual(data.slice(0, 3));
});

it('判断是否全 0', () => {
const data = [{ type: '1', value: 0 }];
expect(isAllZero(data, 'value')).toBe(true);
data.push({ type: '2', value: 0 });
expect(isAllZero(data, 'value')).toBe(true);
data.push({ type: '3', value: null });
expect(isAllZero(data, 'value')).toBe(false);
data.push({ type: '4', value: undefined });
expect(isAllZero(data, 'value')).toBe(false);
});
});
33 changes: 33 additions & 0 deletions examples/dynamic-plots/basic/demo/dynamic-pie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Pie } from '@antv/g2plot';

const data = [
{ type: '分类一', value: 27 },
{ type: '分类二', value: 25 },
{ type: '分类三', value: 18 },
{ type: '分类四', value: 15 },
{ type: '分类五', value: 10 },
{ type: '其他', value: 5 },
];

const piePlot = new Pie('container', {
appendPadding: 10,
data,
angleField: 'value',
colorField: 'type',
radius: 0.9,
label: {
type: 'inner',
offset: '-30%',
content: ({ percent }) => `${(percent * 100).toFixed(0)}%`,
style: {
fontSize: 14,
textAlign: 'center',
},
},
interactions: [{ type: 'element-active' }],
});

piePlot.render();
setInterval(() => {
piePlot.changeData(data.map((d) => ({ ...d, value: d.value * Math.random() })));
}, 1200);
8 changes: 8 additions & 0 deletions examples/dynamic-plots/basic/demo/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@
"en": "Gauge updating with dynamic"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*O2aDSImbWDgAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "dynamic-pie.ts",
"title": {
"zh": "动态更新的饼图",
"en": "Pie updating with dynamic"
},
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/JQXEkyg2Rm/dongtaigengxinshuju-pie-after.gif"
}
]
}
Loading

0 comments on commit 47fa687

Please sign in to comment.