Skip to content

Commit

Permalink
feat: new chart type - box
Browse files Browse the repository at this point in the history
  • Loading branch information
羽熙 committed Aug 2, 2020
1 parent 4e2c047 commit c4a613d
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 0 deletions.
10 changes: 10 additions & 0 deletions __tests__/data/box.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const boxData = [
{ x: 'Oceania', low: 1, q1: 9, median: 16, q3: 22, high: 24 },
{ x: 'East Europe', low: 1, q1: 5, median: 8, q3: 12, high: 16 },
{ x: 'Australia', low: 1, q1: 8, median: 12, q3: 19, high: 26 },
{ x: 'South America', low: 2, q1: 8, median: 12, q3: 21, high: 28 },
{ x: 'North Africa', low: 1, q1: 8, median: 14, q3: 18, high: 24 },
{ x: 'North America', low: 3, q1: 10, median: 17, q3: 28, high: 30 },
{ x: 'West Europe', low: 1, q1: 7, median: 10, q3: 17, high: 22 },
{ x: 'West Africa', low: 1, q1: 6, median: 8, q3: 13, high: 16 },
];
33 changes: 33 additions & 0 deletions __tests__/unit/plots/box/index-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Box } from '../../../../src';
import { boxData } from '../../../data/box';
import { createDiv } from '../../../utils/dom';

const default_range_field = '@@__range';

describe('column', () => {
it('x*range range.min default as 0', () => {
const column = new Box(createDiv('x*range range.min default as 0'), {
width: 400,
height: 500,
data: boxData,
xField: 'x',
yField: ['low', 'q1', 'median', 'q3', 'high'],
});

column.render();

const geometry = column.chart.geometries[0];
const positionFields = geometry.getAttribute('position').getFields();

// 类型
expect(geometry.type).toBe('schema');
// 图形元素个数
expect(column.chart.geometries[0].elements.length).toBe(boxData.length);
// x & range
expect(positionFields).toHaveLength(2);

// range meta default min = 0
// @ts-ignore
expect(geometry.scales[default_range_field].min).toBe(0);
});
});
37 changes: 37 additions & 0 deletions __tests__/unit/plots/box/style-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Box } from '../../../../src';
import { boxData } from '../../../data/box';
import { createDiv } from '../../../utils/dom';

describe('column style', () => {
it('style config', () => {
const column = new Box(createDiv('style config'), {
width: 400,
height: 500,
data: boxData,
xField: 'x',
yField: ['low', 'q1', 'median', 'q3', 'high'],
meta: {
sales: {
nice: true,
formatter: (v) => `${Math.floor(v / 10000)}万`,
},
},
boxStyle: {
stroke: 'black',
lineWidth: 2,
fill: '#1890FF',
},
});

column.render();

const geometry = column.chart.geometries[0];
const elements = geometry.elements;
expect(elements[0].shape.attr('stroke')).toBe('black');
expect(elements[0].shape.attr('lineWidth')).toBe(2);
expect(elements[0].shape.attr('fill')).toBe('#1890FF');
});

// TODO
// it('style callback', () => {});
});
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ export { Bar, BarOptions } from './plots/bar';

// 雷达图及类型定义
export { Radar, RadarOptions } from './plots/radar';

// 箱线图及类型定义
export { Box, BoxOptions } from './plots/box';
127 changes: 127 additions & 0 deletions src/plots/box/adaptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { deepMix, isFunction, isNil } from '@antv/util';
import DataSet from '@antv/data-set';
import { Params } from '../../core/adaptor';
import { findGeometry } from '../../common/helper';
import { BoxOptions } from './types';
import { flow, pick } from '../../utils';
import { AXIS_META_CONFIG_KEYS } from '../../constant';

const RANGE = '@@__range';

/**
* 字段
* @param params
*/
function field(params: Params<BoxOptions>): Params<BoxOptions> {
const { chart, options } = params;
const { xField, yField, data } = options;
const [low, q1, median, q3, high] = yField;

const ds = new DataSet();
const dv = ds.createView().source(data);

// dataset 处理数据
dv.transform({
type: 'map',
callback: (obj) => {
obj[RANGE] = [obj[low], obj[q1], obj[median], obj[q3], obj[high]];
return obj;
},
});

chart.schema().position(`${xField}*${RANGE}`).shape('box');
chart.data(dv.rows);

return params;
}

/**
* meta 配置
* @param params
*/
function meta(params: Params<BoxOptions>): Params<BoxOptions> {
const { chart, options } = params;
const { meta, xAxis, yAxis, xField } = options;

const scales = deepMix(
{
// 箱线图默认 range 从0 开始
[RANGE]: { min: 0 },
},
meta,
{
[xField]: pick(xAxis, AXIS_META_CONFIG_KEYS),
[RANGE]: pick(yAxis, AXIS_META_CONFIG_KEYS),
}
);

chart.scale(scales);

return params;
}

/**
* axis 配置
* @param params
*/
function axis(params: Params<BoxOptions>): Params<BoxOptions> {
const { chart, options } = params;
const { xAxis, yAxis, xField } = options;

// 为 false 则是不显示轴
if (xAxis === false) {
chart.axis(xField, false);
} else {
chart.axis(xField, xAxis);
}

if (yAxis === false) {
chart.axis(RANGE, false);
} else {
chart.axis(RANGE, yAxis);
}

return params;
}

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

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

// return params;
// }

/**
* 样式
* @param params
*/
function style(params: Params<BoxOptions>): Params<BoxOptions> {
const { chart, options } = params;
const { xField, yField, boxStyle } = options;

const geometry = findGeometry(chart, 'schema');
if (boxStyle && geometry) {
if (isFunction(boxStyle)) {
// geometry.style(`${xField}*${yField}*${colorField}`, columnStyle);
} else {
geometry.style(boxStyle);
}
}
return params;
}

/**
* 箱线图适配器
* @param params
*/
export function adaptor(params: Params<BoxOptions>) {
return flow(field, meta, axis, style)(params);
}
18 changes: 18 additions & 0 deletions src/plots/box/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Plot } from '../../core/plot';
import { Adaptor } from '../../core/adaptor';
import { BoxOptions } from './types';
import { adaptor } from './adaptor';

export { BoxOptions };

export class Box extends Plot<BoxOptions> {
/** 图表类型 */
public type: string = 'box';

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

export interface BoxOptions extends Options {
/** x 轴字段 */
readonly xField: string;
/** y 轴映射 box range [low, q1, median, q3, hight] 五个字段 */
readonly yField: [string?, string?, string?, string?, string?];
/** 柱子样式配置,可选 */
readonly boxStyle?: ShapeStyle | ((x: any, y: any, color?: any) => ShapeStyle);
}

0 comments on commit c4a613d

Please sign in to comment.