Skip to content

Commit 1dedf61

Browse files
committed
fix(box): 修复箱形图异常点view的label只能展示一个 (#2913)
* fix(box): 修复箱形图异常点view的label只能展示一个 * docs(box): 丰富 box plot demo * fix(box): box plot 单测修改
1 parent 914d89f commit 1dedf61

File tree

9 files changed

+121
-19
lines changed

9 files changed

+121
-19
lines changed

__tests__/unit/plots/box/change-data-spec.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ describe('box change data', () => {
6060
box.render();
6161
const outliersView = box.chart.views.find((v) => v.id === OUTLIERS_VIEW_ID);
6262

63+
const len = outliersData.reduce((r, d) => r + d.outliers.length, 0);
6364
expect(box.chart.geometries[0].elements.length).toEqual(outliersData.length);
64-
expect(outliersView.geometries[0].elements.length).toEqual(outliersData.length);
65+
expect(outliersView.geometries[0].elements.length).toEqual(len);
6566

6667
const newData = [
6768
...outliersData,

__tests__/unit/plots/box/outliers-spec.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ describe('box outliers', () => {
2626
// 类型
2727
expect(geometry.type).toBe('point');
2828
// 图形元素个数
29-
expect(geometry.elements.length).toBe(outliersData.length);
29+
const len = outliersData.reduce((r, d) => r + d.outliers.length, 0);
30+
expect(geometry.elements.length).toBe(len);
3031
// 同步y轴度量 axis sync
3132
// @ts-ignore
3233
expect(outliersScale.sync).toEqual(BOX_SYNC_NAME);
@@ -59,7 +60,7 @@ describe('box outliers', () => {
5960
const elements = view.geometries[0].elements;
6061

6162
// 类型
62-
expect(elements[0].shape.cfg.children[0].attr('fill')).toBe('#f6f');
63+
expect(elements[0].shape.attr('fill')).toBe('#f6f');
6364

6465
box.destroy();
6566
});

docs/common/label.en.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
| labelLine | _null_ \| _boolean_ \| _LabelLineCfg_ | Used to set the style property of the text connector. NULL indicates that it is not displayed. |
1414
| labelEmit | _boolean_ | Only applies to text in polar coordinates, indicating whether the text is radially displayed according to the Angle. True means on and false means off |
1515
| layout | _'overlap' \| 'fixedOverlap' \| 'limitInShape'_ | Text layout type, support a variety of layout function combination. |
16-
| position | _'top' \| 'bottom' \| 'middle' \| 'left' \| 'right'_ | Specifies the position of the current Label relative to the current graphic (Only works for column plot and bar plot, which geometry is interval) |
16+
| position | _'top' \| 'bottom' \| 'middle' \| 'left' \| 'right'_ | Specifies the position of the current Label relative to the current graphic (💡 Attention: Only works for **column plot** and **bar plot**, which geometry is interval) |
1717
| animate | _boolean \| AnimateOption_ | Animation configuration. |
1818
| formatter | _Function_ | Format function |
1919
| autoHide | _boolean_ | Whether to hide it automatically, default to false |

docs/common/label.zh.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
| labelLine | _null_ \| _boolean_ \| _LabelLineCfg_ | 用于设置文本连接线的样式属性,null 表示不展示。 |
1414
| labelEmit | _boolean_ | 只对极坐标下的文本生效,表示文本是否按照角度进行放射状显示,true 表示开启,false 表示关闭 |
1515
| layout | _'overlap' \| 'fixedOverlap' \| 'limitInShape'_ | 文本布局类型,支持多种布局函数组合使用。 |
16-
| position | _'top' \| 'bottom' \| 'middle' \| 'left' \| 'right'_ | 指定当前 label 与当前图形的相对位置 (只对 geometry 为 interval 的 柱条形图生效) |
16+
| position | _'top' \| 'bottom' \| 'middle' \| 'left' \| 'right'_ | 指定当前 label 与当前图形的相对位置 (💡 注意:只对 geometry 为 interval 的柱条形图生效) |
1717
| animate | _boolean \| AnimateOption_ | 动画配置。 |
1818
| formatter | _Function_ | 格式化函数 |
1919
| autoHide | _boolean_ | 是否自动隐藏,默认 false |

examples/more-plots/box/demo/label.ts

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Box } from '@antv/g2plot';
2+
3+
const data = [
4+
{ x: '职业 A', low: 20000, q1: 26000, median: 27000, q3: 32000, high: 38000, outliers: [50000, 52000] },
5+
{ x: '职业 B', low: 40000, q1: 49000, median: 62000, q3: 73000, high: 88000, outliers: [32000, 29000, 106000] },
6+
{ x: '职业 C', low: 52000, q1: 59000, median: 65000, q3: 74000, high: 83000, outliers: [91000] },
7+
{ x: '职业 D', low: 58000, q1: 96000, median: 130000, q3: 170000, high: 200000, outliers: [42000, 210000, 215000] },
8+
{ x: '职业 E', low: 24000, q1: 28000, median: 32000, q3: 38000, high: 42000, outliers: [48000] },
9+
{ x: '职业 F', low: 47000, q1: 56000, median: 69000, q3: 85000, high: 100000, outliers: [110000, 115000, 32000] },
10+
{ x: '职业 G', low: 64000, q1: 74000, median: 83000, q3: 93000, high: 100000, outliers: [110000] },
11+
{ x: '职业 H', low: 67000, q1: 72000, median: 84000, q3: 95000, high: 110000, outliers: [57000, 54000] },
12+
];
13+
14+
const outliersBoxPlot = new Box('container', {
15+
data,
16+
xField: 'x',
17+
yField: ['low', 'q1', 'median', 'q3', 'high'],
18+
outliersField: 'outliers',
19+
outliersStyle: {
20+
fill: '#f6f',
21+
},
22+
label: {
23+
style: {
24+
textBaseline: 'top',
25+
},
26+
layout: { type: 'hide-overlap' },
27+
},
28+
});
29+
30+
outliersBoxPlot.render();

examples/more-plots/box/demo/meta.json

+17
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@
3535
"en": "Set alias of field"
3636
},
3737
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/BUWqiUYOhY/box-alias.png"
38+
},
39+
{
40+
"filename": "label.ts",
41+
"title": {
42+
"zh": "设置箱线图 label",
43+
"en": "Box plot label"
44+
},
45+
"new": true,
46+
"screenshot": ""
47+
},
48+
{
49+
"filename": "tooltip.ts",
50+
"title": {
51+
"zh": "设置箱线图 tooltip",
52+
"en": "Box plot tooltip"
53+
},
54+
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*cE6vR461omUAAAAAAAAAAAAAARQnAQ"
3855
}
3956
]
4057
}
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Box } from '@antv/g2plot';
2+
3+
const data = [
4+
{ x: '职业 A', low: 20000, q1: 26000, median: 27000, q3: 32000, high: 38000, outliers: [50000, 52000] },
5+
{ x: '职业 B', low: 40000, q1: 49000, median: 62000, q3: 73000, high: 88000, outliers: [32000, 29000, 106000] },
6+
{ x: '职业 C', low: 52000, q1: 59000, median: 65000, q3: 74000, high: 83000, outliers: [91000] },
7+
{ x: '职业 D', low: 58000, q1: 96000, median: 130000, q3: 170000, high: 200000, outliers: [42000, 210000, 215000] },
8+
{ x: '职业 E', low: 24000, q1: 28000, median: 32000, q3: 38000, high: 42000, outliers: [48000] },
9+
{ x: '职业 F', low: 47000, q1: 56000, median: 69000, q3: 85000, high: 100000, outliers: [110000, 115000, 32000] },
10+
{ x: '职业 G', low: 64000, q1: 74000, median: 83000, q3: 93000, high: 100000, outliers: [110000] },
11+
{ x: '职业 H', low: 67000, q1: 72000, median: 84000, q3: 95000, high: 110000, outliers: [57000, 54000] },
12+
];
13+
14+
const boxPlot = new Box('container', {
15+
width: 400,
16+
height: 500,
17+
data: data,
18+
xField: 'x',
19+
yField: ['low', 'q1', 'median', 'q3', 'high'],
20+
meta: {
21+
low: {
22+
alias: '最低值',
23+
},
24+
q1: {
25+
alias: '下四分位数',
26+
},
27+
median: {
28+
alias: '最低值',
29+
},
30+
q3: {
31+
alias: '上四分位数',
32+
},
33+
high: {
34+
alias: '最高值',
35+
},
36+
outliers: {
37+
alias: '异常值',
38+
},
39+
},
40+
outliersField: 'outliers',
41+
tooltip: {
42+
fields: ['high', 'q3', 'median', 'q1', 'low', 'outliers'],
43+
},
44+
boxStyle: {
45+
stroke: '#545454',
46+
fill: '#1890FF',
47+
fillOpacity: 0.3,
48+
},
49+
animation: false,
50+
});
51+
52+
boxPlot.render();

examples/plugin/pattern/demo/meta.json

+1-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
"zh": "带贴图图案的饼图",
1111
"en": "Pie plot with pattern"
1212
},
13-
"new": true,
1413
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/cACfJO99IO/32b38815-8427-4111-a0f0-34208aa65dd8.png"
1514
},
1615
{
@@ -19,7 +18,6 @@
1918
"zh": "图例 marker 设置贴图图案",
2019
"en": "Custom legend marker with pattern"
2120
},
22-
"new": true,
2321
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/N%26xo4t7zRW/79c6bdf8-0cc6-49b7-94a2-f462b9f825bc.png"
2422
},
2523
{
@@ -28,7 +26,6 @@
2826
"zh": "带贴图图案的分组柱状图",
2927
"en": "Grouped column plot with pattern"
3028
},
31-
"new": true,
3229
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/wlurjHqyu%24/4b21f53e-7186-42c8-8f57-6f32c91a09ba.png"
3330
},
3431
{
@@ -37,7 +34,6 @@
3734
"zh": "带贴图图案的条形图",
3835
"en": "Bar plot with pattern"
3936
},
40-
"new": true,
4137
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/APiY4THavu/93d5749b-1e0d-4118-b04f-4e928a7eb144.png"
4238
},
4339
{
@@ -46,7 +42,6 @@
4642
"zh": "带贴图图案的堆叠图",
4743
"en": "Stack-bar plot with pattern"
4844
},
49-
"new": true,
5045
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/LGFxwRRwZl/d11e9835-a520-4a4c-9893-30b6a6ebb3c1.png"
5146
},
5247
{
@@ -55,7 +50,6 @@
5550
"zh": "带贴图图案的环形图",
5651
"en": "Donut with pattern"
5752
},
58-
"new": true,
5953
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/spgIo%26zAzG/6562dc40-2ac6-4c8a-b433-88e41e20621a.png"
6054
},
6155
{
@@ -64,7 +58,6 @@
6458
"zh": "带贴图图案的面积图",
6559
"en": "Area with pattern"
6660
},
67-
"new": true,
6861
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/msiCPIX%243T/16b69b77-6a87-42e0-9e8c-9767d6b9df79.png"
6962
},
7063
{
@@ -73,7 +66,6 @@
7366
"zh": "带贴图图案的堆叠分组柱状图",
7467
"en": "Stacked grouped column plot with pattern"
7568
},
76-
"new": true,
7769
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/1Ei8A%24VtRI/a9f53248-bdb4-4bc4-a567-3cd0d76e2a26.png"
7870
},
7971
{
@@ -82,7 +74,6 @@
8274
"zh": "带贴图图案的热力图",
8375
"en": "Heatmap with pattern"
8476
},
85-
"new": true,
8677
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/0zXfW5rjOu/e442fed3-047c-4f34-9536-8efe2e60a92f.png"
8778
},
8879
{
@@ -91,7 +82,6 @@
9182
"zh": "带贴图图案的热力图",
9283
"en": "Heatmap with pattern (cookie)"
9384
},
94-
"new": true,
9585
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/vR9mVPFTmx/7b2e0aac-cee0-428b-bca3-c7082bb2b76f.png"
9686
},
9787
{
@@ -100,8 +90,7 @@
10090
"zh": "带贴图图案的水波图",
10191
"en": "Liquid with pattern"
10292
},
103-
"new": true,
10493
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/CBo9L7m9Ux/53202fa4-ad16-4c47-ba72-f8b9858f0c78.png"
10594
}
10695
]
107-
}
96+
}

src/plots/box/adaptor.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { isArray } from '@antv/util';
2+
import { Types } from '@antv/g2';
23
import { Params } from '../../core/adaptor';
34
import { interaction, animation, theme, tooltip } from '../../adaptor/common';
45
import { point, schema } from '../../adaptor/geometries';
@@ -34,6 +35,8 @@ function field(params: Params<BoxOptions>): Params<BoxOptions> {
3435
seriesField: groupField,
3536
tooltip: tooltipOptions,
3637
rawFields,
38+
// 只有异常点视图展示 label
39+
label: false,
3740
schema: {
3841
shape: 'box',
3942
color,
@@ -50,21 +53,30 @@ function field(params: Params<BoxOptions>): Params<BoxOptions> {
5053
return params;
5154
}
5255

56+
/**
57+
* 创建异常点 view
58+
*/
5359
function outliersPoint(params: Params<BoxOptions>): Params<BoxOptions> {
5460
const { chart, options } = params;
55-
const { xField, data, outliersField, outliersStyle, padding } = options;
61+
const { xField, data, outliersField, outliersStyle, padding, label } = options;
5662

5763
if (!outliersField) return params;
5864

5965
const outliersView = chart.createView({ padding, id: OUTLIERS_VIEW_ID });
60-
outliersView.data(data);
66+
const outliersViewData = data.reduce((ret, datum) => {
67+
const outliersData = datum[outliersField];
68+
outliersData.forEach((d) => ret.push({ ...datum, [outliersField]: d }));
69+
return ret;
70+
}, []) as Types.Datum[];
6171

72+
outliersView.data(outliersViewData);
6273
point({
6374
chart: outliersView,
6475
options: {
6576
xField,
6677
yField: outliersField,
6778
point: { shape: 'circle', style: outliersStyle },
79+
label,
6880
},
6981
});
7082

0 commit comments

Comments
 (0)