Skip to content

Feat pie #1290

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}>;