Skip to content

Commit

Permalink
Feat pie (#1290)
Browse files Browse the repository at this point in the history
* chore: test-live 添加 watch 参数

* feat(types): 共同类型定义

* feat(common): 抽取共同 adaptor 构建器

[x] 支持 tooltip

* feat(pie-plot): 新增 饼图

[x] angleField, colorField, legend, tooltip, pieStyle, etc

* fix(pie-plot): fix cr suggestions

Co-authored-by: xinming <[email protected]>
  • Loading branch information
visiky and xinming authored Jul 14, 2020
1 parent d89ecf6 commit f7b68f8
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 19 deletions.
141 changes: 141 additions & 0 deletions __tests__/unit/plots/pie/index-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { Pie } from '../../../../src';
import { POSITIVE_NEGATIVE_DATA } from '../../../data/common';
import { createDiv } from '../../../utils/dom';

describe('pie', () => {
const data = POSITIVE_NEGATIVE_DATA.filter((o) => o.value > 0).map((d, idx) =>
idx === 1 ? { ...d, type: 'item1' } : d
);
it('angleField: single color', () => {
const pie = new Pie(createDiv(), {
width: 400,
height: 300,
data,
angleField: 'value',
radius: 0.8,
});

pie.render();

const geometry = pie.chart.geometries[0];
const elements = geometry.elements;

expect(elements.length).toBe(data.length);
expect(elements[0].getModel().color).toBe(elements[1].getModel().color);
});

it('angleField with colorField: multiple colors', () => {
const pie = new Pie(createDiv(), {
width: 400,
height: 300,
data,
angleField: 'value',
colorField: 'type',
color: ['blue', 'red', 'yellow', 'lightgreen', 'lightblue', 'pink'],
radius: 0.8,
});

pie.render();

const geometry = pie.chart.geometries[0];
const elements = geometry.elements;
// @ts-ignore
expect(elements.length).toBe(data.length);
// 绘图数据
expect(elements[0].getModel().style?.fill || elements[0].getModel().color).toBe('blue');
expect(elements[1].getModel().style?.fill || elements[1].getModel().color).toBe('red');
});

it('no radius', () => {
const pie = new Pie(createDiv(), {
width: 400,
height: 300,
data,
angleField: 'value',
colorField: 'type',
});

pie.render();

const coordinate = pie.chart.getCoordinate();
const { radius } = coordinate;
const polarRadius = coordinate.getRadius();
expect(radius).toBeUndefined();
expect(polarRadius).toBeGreaterThan(0);

const geometry = pie.chart.geometries[0];
const elements = geometry.elements;
});

it('innerRadius', () => {
const pie = new Pie(createDiv(), {
width: 400,
height: 300,
data,
angleField: 'value',
colorField: 'type',
color: ['blue', 'red', 'yellow', 'lightgreen', 'lightblue', 'pink'],
radius: 0.8,
innerRadius: 0.5,
});

pie.render();

const coordinate = pie.chart.getCoordinate();
const { innerRadius, radius } = coordinate;
expect(innerRadius).toBe((radius / 0.8) * 0.5);
});

it('pieStyle: custom style of pie', () => {
const pie = new Pie(createDiv(), {
width: 400,
height: 300,
data,
angleField: 'value',
colorField: 'type',
color: ['blue', 'red', 'yellow', 'lightgreen', 'lightblue', 'pink'],
radius: 0.8,
innerRadius: 0.5,
pieStyle: {
fill: 'red',
lineWidth: 3,
stroke: 'yellow',
},
});

pie.render();

const geometry = pie.chart.geometries[0];
const elements = geometry.elements;
expect(elements[0].getModel().style?.fill).toBe('red');
expect(elements[1].getModel().style?.fill).toBe('red');
expect(elements[1].getModel().style?.lineWidth).toBe(3);
expect(elements[1].getModel().style?.stroke).toBe('yellow');
});

it('pieStyle: with callback', () => {
const pie = new Pie(createDiv(), {
width: 400,
height: 300,
data,
angleField: 'value',
colorField: 'type',
color: ['blue', 'red', 'yellow', 'lightgreen', 'lightblue', 'pink'],
radius: 0.8,
innerRadius: 0.5,
pieStyle: (item) => ({
fill: item === 'item1' ? 'blue' : 'red',
lineWidth: 3,
stroke: 'yellow',
}),
});

pie.render();

const geometry = pie.chart.geometries[0];
const elements = geometry.elements;
expect(elements[0].getModel().style?.fill).toBe('red');
expect(elements[1].getModel().style?.fill).toBe('blue');
expect(elements[2].getModel().style?.fill).toBe('red');
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"lint": "eslint --ext .ts ./src ./__tests__",
"lint-staged": "lint-staged",
"test": "jest",
"test-live": "DEBUG_MODE=1 jest ./__tests__",
"test-live": "DEBUG_MODE=1 jest --watch ./__tests__",
"coverage": "jest --coverage",
"ci": "run-s lint build coverage",
"changelog": "conventional-changelog -i CHANGELOG.md -a -s",
Expand Down
20 changes: 20 additions & 0 deletions src/common/adaptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @file 通用的一些 adaptor
*/
import { Params } from '../core/adaptor';
import { Options } from '../types';

/**
* 通用 tooltip 配置
* @param params
*/
export function tooltip<O extends Options>(params: Params<O>): Params<O> {
const { chart, options } = params;
const { tooltip } = options;

if (tooltip) {
chart.tooltip(tooltip);
}

return params;
}
2 changes: 1 addition & 1 deletion src/core/adaptor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Chart } from '@antv/g2';
import { Chart, Geometry } from '@antv/g2';
import { Options } from '../types';

/**
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export * from './types';

// 折线图及类型定义
export { Line, LineOptions } from './plots/line';
// 饼图及类型定义
export { Pie, PieOptions } from './plots/pie';

//散点图及类型定义
export { Scatter, ScatterOptions } from './plots/scatter';
16 changes: 1 addition & 15 deletions src/plots/line/adaptor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { deepMix } from '@antv/util';
import { Params } from '../../core/adaptor';
import { tooltip } from '../../common/adaptor';
import { flow, pick } from '../../utils';
import { LineOptions } from './types';

Expand Down Expand Up @@ -81,21 +82,6 @@ function legend(params: Params<LineOptions>): Params<LineOptions> {
return params;
}

/**
* tooltip 配置
* @param params
*/
function tooltip(params: Params<LineOptions>): Params<LineOptions> {
const { chart, options } = params;
const { tooltip } = options;

if (tooltip) {
chart.tooltip(tooltip);
}

return params;
}

/**
* 折线图适配器
* @param chart
Expand Down
106 changes: 106 additions & 0 deletions src/plots/pie/adaptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { deepMix, isFunction } from '@antv/util';
import { Params } from '../../core/adaptor';
import { tooltip } from '../../common/adaptor';
import { flow } from '../../utils';
import { PieOptions } from './types';

/**
* 字段
* @param params
*/
function field(params: Params<PieOptions>): Params<PieOptions> {
const { chart, options } = params;
const { data, angleField, colorField, color } = options;

// TODO 饼图数据非法处理
chart.data(data);
const geometry = chart.interval().position(`1*${angleField}`).adjust({ type: 'stack' });

if (colorField) {
geometry.color(colorField, color);
}

return params;
}

/**
* meta 配置
* @param params
*/
function meta(params: Params<PieOptions>): Params<PieOptions> {
const { chart, options } = params;
const { meta, colorField } = options;

// meta 直接是 scale 的信息
const scales = deepMix({}, meta);
chart.scale(scales, {
[colorField]: { type: 'cat' },
});

return params;
}

/**
* coord 配置
* @param params
*/
function coord(params: Params<PieOptions>): Params<PieOptions> {
const { chart, options } = params;
const { radius, innerRadius } = options;

chart.coordinate({
type: 'theta',
cfg: {
radius,
innerRadius,
},
});

return params;
}

/**
* legend 配置
* @param params
*/
function legend(params: Params<PieOptions>): Params<PieOptions> {
const { chart, options } = params;
const { legend, colorField } = options;

if (legend && colorField) {
chart.legend(colorField, legend);
}

return params;
}

/**
* style 配置
* @param params
*/
function style(params: Params<PieOptions>): Params<PieOptions> {
const { chart, options } = params;
const { pieStyle, angleField, colorField } = options;

const geometry = chart.geometries[0];
if (pieStyle && geometry) {
if (isFunction(pieStyle)) {
// 为了兼容,colorField 放第一位
geometry.style(colorField ? `${colorField}*${angleField}` : angleField, pieStyle);
} else {
geometry.style(pieStyle);
}
}

return params;
}

/**
* 折线图适配器
* @param chart
* @param options
*/
export function adaptor(params: Params<PieOptions>) {
// flow 的方式处理所有的配置到 G2 API
flow(field, meta, coord, legend, tooltip, style)(params);
}
18 changes: 18 additions & 0 deletions src/plots/pie/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Plot } from '../../core/plot';
import { PieOptions } from './types';
import { adaptor } from './adaptor';
import { Adaptor } from '../../core/adaptor';

export { PieOptions };

export class Pie extends Plot<PieOptions> {
/** 图表类型 */
public type: string = 'pie';

/**
* 获取 饼图 的适配器
*/
protected getSchemaAdaptor(): Adaptor<PieOptions> {
return adaptor;
}
}
13 changes: 13 additions & 0 deletions src/plots/pie/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Options } from '../../types';
import { ShapeStyle } from '../../types/style';

export interface PieOptions extends Options {
/** 角度映射字段 */
readonly angleField: string;
readonly colorField?: string;
readonly radius?: number;
readonly innerRadius?: number;

/** 饼图图形样式 */
readonly pieStyle?: ShapeStyle | ((...args: string[]) => ShapeStyle);
}
12 changes: 10 additions & 2 deletions src/types/style.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
/** G shape style 配置 */
export type ShapeStyle = {};
/** G shape style 配置, 按道理应该从 G 中引入 */
export type ShapeStyle = Readonly<{
fill?: string;
stroke?: string;
lineWidth?: number;
lineDash?: number[];
opacity?: number;
fillOpacity?: number;
strokeOpacity?: number;
}>;

0 comments on commit f7b68f8

Please sign in to comment.