Skip to content

Commit

Permalink
feat(box): 重构和增强箱线图 (#2583)
Browse files Browse the repository at this point in the history
* docs(legend): 图例文档

* refactor(box): 使用 geometry adaptor 处理 box,优化 tooltip & 增加单测

* docs(box): 箱线图增加设置字段别名的 demo
  • Loading branch information
visiky authored May 31, 2021
1 parent 34e3e83 commit 6afc12c
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 103 deletions.
89 changes: 63 additions & 26 deletions __tests__/unit/plots/box/tooltip-spec.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,88 @@
import { Box } from '../../../../src';
import { boxData, groupBoxData } from '../../../data/box';
import { createDiv } from '../../../utils/dom';
import { createDiv, removeDom } from '../../../utils/dom';

describe('box tooltip', () => {
it('x*y and tooltip', () => {
const box = new Box(createDiv('box tooltip'), {
width: 400,
height: 500,
data: boxData,
xField: 'x',
yField: ['low', 'q1', 'median', 'q3', 'high'],
tooltip: {
title: 'hello world',
},
});
const div = createDiv('');
const box = new Box(div, {
width: 400,
height: 500,
data: boxData,
xField: 'x',
yField: ['low', 'q1', 'median', 'q3', 'high'],
tooltip: {
title: 'hello world',
},
});

box.render();
box.render();

it('tooltip: custom title', () => {
// @ts-ignore
expect(box.chart.options.tooltip.title).toBe('hello world');
const bbox = box.chart.geometries[0].elements[0].getBBox();

box.chart.showTooltip({ x: bbox.width / 2 + bbox.x, y: bbox.height / 2 + bbox.y });
expect(div.querySelectorAll('.g2-tooltip-list-item').length).toBe(5);
box.chart.hideTooltip();
});

it('tooltip: fields', () => {
box.update({ tooltip: { fields: ['low', 'q1', 'median'] } });
const bbox = box.chart.geometries[0].elements[0].getBBox();

box.chart.showTooltip({ x: bbox.width / 2 + bbox.x, y: bbox.height / 2 + bbox.y });
expect(div.querySelectorAll('.g2-tooltip-list-item').length).toBe(3);
box.chart.hideTooltip();
});

it('tooltip: fields & customContent', () => {
box.update({
...box.options,
tooltip: false,
tooltip: {
fields: ['low', 'q1', 'median'],
customContent: (text, items) =>
`<div>${items.map((item, idx) => `<div class="custom-tooltip-item-content">${idx}</div>`)}<div>`,
},
});
// @ts-ignore
expect(box.chart.options.tooltip).toBe(false);
expect(box.chart.getComponents().find((co) => co.type === 'tooltip')).toBe(undefined);

box.destroy();
const bbox = box.chart.geometries[0].elements[0].getBBox();
box.chart.showTooltip({ x: bbox.width / 2 + bbox.x, y: bbox.height / 2 + bbox.y });
expect(div.getElementsByClassName('custom-tooltip-item-content').length).toBe(3);

// 设置hide
box.chart.hideTooltip();
});

it('default toolip', () => {
const box = new Box(createDiv('default tooltip'), {
width: 400,
height: 500,
it('tooltip: groupField', () => {
box.update({
data: groupBoxData,
xField: 'type',
yField: '_bin',
groupField: 'Species',
tooltip: { fields: undefined, customContent: undefined },
});

box.render();
// @ts-ignore
expect(box.chart.options.tooltip.shared).toBe(true);
// @ts-ignore 箱线图 默认不展示 crosshairs(ugly)
expect(box.chart.options.tooltip.showCrosshairs).toBeUndefined();

const bbox = box.chart.geometries[0].elements[0].getBBox();
box.chart.showTooltip({ x: bbox.width / 2 + bbox.x, y: bbox.height / 2 + bbox.y });

expect(div.querySelectorAll('.g2-tooltip-list-item').length).toBe(3);
expect((div.querySelector('.g2-tooltip-name') as HTMLElement).innerText).toBe('I. setosa');
box.chart.hideTooltip();
});

it('tooltip: false', () => {
box.update({ tooltip: false });
// @ts-ignore
expect(box.chart.options.tooltip.showCrosshairs).toBe(true);
expect(box.chart.options.tooltip).toBe(false);
expect(box.chart.getComponents().find((co) => co.type === 'tooltip')).toBe(undefined);
});

afterAll(() => {
box.destroy();
removeDom(div);
});
});
2 changes: 1 addition & 1 deletion docs/common/legend-cfg.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ Apply to <tag color="cyan" text="Continuous legend">Continuous legend</tag>, sel

Apply to <tag color="cyan" text="Continuous legend">Continuous legend</tag>, 当前选中的范围.

##### legendOption.selected ✨ 🆕
##### selected ✨ 🆕

<description> _object_ **optional** </description>

Expand Down
2 changes: 1 addition & 1 deletion docs/common/legend-cfg.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ pageNavigator: {

适用于 <tag color="cyan" text="连续图例">连续图例</tag>,当前选中的范围。

##### legendOption.selected ✨ 🆕
##### selected ✨ 🆕

<description> _object_ **optional** </description>

Expand Down
48 changes: 48 additions & 0 deletions examples/more-plots/box/demo/meta-alias.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Box } from '@antv/g2plot';

const data = [
{ 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 },
];

const boxPlot = new Box('container', {
width: 400,
height: 500,
data: data,
xField: 'x',
yField: ['low', 'q1', 'median', 'q3', 'high'],
meta: {
low: {
alias: '最低值',
},
q1: {
alias: '下四分位数',
},
median: {
alias: '最低值',
},
q3: {
alias: '上四分位数',
},
high: {
alias: '最高值',
},
},
tooltip: {
fields: ['high', 'q3', 'median', 'q1', 'low'],
},
boxStyle: {
stroke: '#545454',
fill: '#1890FF',
fillOpacity: 0.3,
},
animation: false,
});

boxPlot.render();
10 changes: 9 additions & 1 deletion examples/more-plots/box/demo/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
"en": "Box plot with error"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*cE6vR461omUAAAAAAAAAAAAAARQnAQ"
},
{
"filename": "meta-alias.ts",
"title": {
"zh": "设置字段别名",
"en": "Set alias of field"
},
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/BUWqiUYOhY/box-alias.png"
}
]
}
}
114 changes: 41 additions & 73 deletions src/plots/box/adaptor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isFunction, isObject } from '@antv/util';
import { isArray } from '@antv/util';
import { Params } from '../../core/adaptor';
import { interaction, animation, theme } from '../../adaptor/common';
import { findGeometry } from '../../utils';
import { interaction, animation, theme, tooltip } from '../../adaptor/common';
import { point, schema } from '../../adaptor/geometries';
import { flow, pick, deepAssign } from '../../utils';
import { AXIS_META_CONFIG_KEYS } from '../../constant';
import { BoxOptions } from './types';
Expand All @@ -14,18 +14,38 @@ import { transformData } from './utils';
*/
function field(params: Params<BoxOptions>): Params<BoxOptions> {
const { chart, options } = params;
const { xField, yField, groupField, color } = options;
const { xField, yField, groupField, color, tooltip, boxStyle } = options;

const yFieldName = Array.isArray(yField) ? BOX_RANGE : yField;
chart.data(transformData(options.data, yField));

const geometry = chart.schema().position(`${xField}*${yFieldName}`).shape('box');
const yFieldName = isArray(yField) ? BOX_RANGE : yField;
const rawFields = yField ? (isArray(yField) ? yField : [yField]) : [];

// set group field as color channel
if (groupField) {
geometry.color(groupField, color).adjust('dodge');
let tooltipOptions = tooltip;
if (tooltipOptions !== false) {
tooltipOptions = deepAssign({}, { fields: isArray(yField) ? yField : [] }, tooltipOptions);
}

chart.data(transformData(options.data, yField));
const { ext } = schema(
deepAssign({}, params, {
options: {
xField,
yField: yFieldName,
seriesField: groupField,
tooltip: tooltipOptions,
rawFields,
schema: {
shape: 'box',
color,
style: boxStyle,
},
},
})
);

if (groupField) {
ext.geometry.adjust('dodge');
}

return params;
}
Expand All @@ -38,24 +58,17 @@ function outliersPoint(params: Params<BoxOptions>): Params<BoxOptions> {

const outliersView = chart.createView({ padding, id: OUTLIERS_VIEW_ID });
outliersView.data(data);

point({
chart: outliersView,
options: {
xField,
yField: outliersField,
point: { shape: 'circle', style: outliersStyle },
},
});

outliersView.axis(false);
const geometry = outliersView.point().position(`${xField}*${outliersField}`).shape('circle');

/**
* style 的几种情况
* g.style({ fill: 'red' });
* g.style('x*y*color', (x, y, color) => ({ fill: 'red' }));
*/
if (isFunction(outliersStyle)) {
geometry.style(`${xField}*${outliersField}`, (_x: string, _outliers: number) => {
return outliersStyle({
[xField]: _x,
[outliersField]: _outliers,
});
});
} else if (isObject(outliersStyle)) {
geometry.style(outliersStyle);
}

return params;
}
Expand Down Expand Up @@ -137,55 +150,10 @@ export function legend(params: Params<BoxOptions>): Params<BoxOptions> {
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');
const yFieldName = Array.isArray(yField) ? BOX_RANGE : yField;

/**
* style 的几种情况
* g.style({ fill: 'red' });
* g.style('x*y*color', (x, y, color) => ({ fill: 'red' }));
*/
if (isFunction(boxStyle)) {
geometry.style(`${xField}*${yFieldName}`, (_x: string, _y: number) => {
return boxStyle({
[xField]: _x,
[yFieldName]: _y,
});
});
} else if (isObject(boxStyle)) {
geometry.style(boxStyle);
}

return params;
}

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

if (tooltip !== undefined) {
chart.tooltip(tooltip);
}

return params;
}

/**
* 箱型图适配器
* @param params
*/
export function adaptor(params: Params<BoxOptions>) {
return flow(field, outliersPoint, meta, axis, style, legend, tooltip, interaction, animation, theme)(params);
return flow(field, outliersPoint, meta, axis, legend, tooltip, interaction, animation, theme)(params);
}
4 changes: 3 additions & 1 deletion src/plots/box/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export const DEFAULT_OPTIONS = deepAssign({}, Plot.getDefaultOptions(), {
// 默认 tooltips 共享,不显示 markers
tooltip: {
showMarkers: false,
showCrosshairs: true,
shared: true,
},
boxStyle: {
lineWidth: 1,
},
});

0 comments on commit 6afc12c

Please sign in to comment.