diff --git a/__tests__/data/radar.ts b/__tests__/data/radar.ts index c1c61541aa..ee5ce42137 100644 --- a/__tests__/data/radar.ts +++ b/__tests__/data/radar.ts @@ -1,12 +1,22 @@ -const categories = ['销售', '市场营销', '发展', '客户支持', '信息技术', '行政管理']; -/** 预算支出 */ -const data1 = [43000, 19000, 60000, 35000, 17000, 10000]; -/** 实际支出 */ -const data2 = [50000, 39000, 42000, 31000, 26000, 14000]; - -export const SINGLE_DATA = categories.map((d, idx) => ({ name: d, value: data1[idx] })); -export const SERIES_DATA = []; -categories.forEach((d, idx) => { - SERIES_DATA.push({ name: d, value: data1[idx], type: '预算支出' }); - SERIES_DATA.push({ name: d, value: data2[idx], type: '实际支出' }); -}); +export const SINGLE_DATA = [ + { name: '销售', value: 43000 }, + { name: '市场营销', value: 19000 }, + { name: '发展', value: 60000 }, + { name: '客户支持', value: 35000 }, + { name: '信息技术', value: 17000 }, + { name: '行政管理', value: 10000 }, +]; +export const SERIES_DATA = [ + { name: '销售', value: 43000, type: '预算支出' }, + { name: '销售', value: 50000, type: '实际支出' }, + { name: '市场营销', value: 19000, type: '预算支出' }, + { name: '市场营销', value: 39000, type: '实际支出' }, + { name: '发展', value: 60000, type: '预算支出' }, + { name: '发展', value: 42000, type: '实际支出' }, + { name: '客户支持', value: 35000, type: '预算支出' }, + { name: '客户支持', value: 31000, type: '实际支出' }, + { name: '信息技术', value: 17000, type: '预算支出' }, + { name: '信息技术', value: 26000, type: '实际支出' }, + { name: '行政管理', value: 10000, type: '预算支出' }, + { name: '行政管理', value: 14000, type: '实际支出' }, +]; diff --git a/__tests__/unit/plots/pie/interaction-spec.ts b/__tests__/unit/plots/pie/interaction-spec.ts index 0f491dd05e..a99869d320 100644 --- a/__tests__/unit/plots/pie/interaction-spec.ts +++ b/__tests__/unit/plots/pie/interaction-spec.ts @@ -43,7 +43,7 @@ describe('register interaction', () => { context.event = { data: { data: { type: 'item3', value: 13 } } }; action.change(); - delay(5000); + delay(500); const annotations = context.view.getComponents().filter((co) => co.type === 'annotation'); expect(annotations[0].extra.content).toBe('item3'); expect(annotations[1].extra.content).toBe(13); @@ -52,7 +52,7 @@ describe('register interaction', () => { it('触发 pie-statistic:reset', async () => { action.reset(); - delay(5000); + delay(500); const annotations = context.view.getComponents().filter((co) => co.type === 'annotation'); expect(annotations[0].extra.content).toBe('Total'); }); diff --git a/__tests__/unit/plots/pie/statistic-spec.ts b/__tests__/unit/plots/pie/statistic-spec.ts index dcdeaecd13..70ecb7f0dc 100644 --- a/__tests__/unit/plots/pie/statistic-spec.ts +++ b/__tests__/unit/plots/pie/statistic-spec.ts @@ -112,7 +112,7 @@ describe('中心文本 - 指标卡', () => { }); it('自定义中心文本内容: update statistic title & content, 动态数据', async () => { - await delay(5000); + await delay(500); pie.update({ ...pie.options, statistic: { @@ -161,7 +161,7 @@ describe('中心文本 - 指标卡', () => { }); it('自定义中心文本样式: update statistic title style & content style', async () => { - await delay(5000); + await delay(500); pie.update({ ...pie.options, statistic: { @@ -249,7 +249,7 @@ describe('中心文本 - 指标卡', () => { // expect(annotations.length).toBe(1); // expect(annotations[0].component.get('type')).toBe('image'); - // await delay(5000); + // await delay(500); // pie.update({ // ...pie.options, // annotations: [ diff --git a/__tests__/unit/plots/radar/tooltip-spec.ts b/__tests__/unit/plots/radar/tooltip-spec.ts index cf9d2f9ecb..74edca2bd4 100644 --- a/__tests__/unit/plots/radar/tooltip-spec.ts +++ b/__tests__/unit/plots/radar/tooltip-spec.ts @@ -1,5 +1,5 @@ import { Radar } from '../../../../src'; -import { SERIES_DATA } from '../../../data/radar'; +import { SINGLE_DATA, SERIES_DATA } from '../../../data/radar'; import { createDiv } from '../../../utils/dom'; describe('radar tooltip', () => { @@ -25,3 +25,118 @@ describe('radar tooltip', () => { expect(radar.chart.options.tooltip.showCrosshairs).toBe(true); }); }); + +describe('radar, 自定义 tooltip', () => { + it('xField*yField', () => { + const radar = new Radar(createDiv(), { + width: 400, + height: 300, + data: SINGLE_DATA, + xField: 'name', + yField: 'value', + radius: 0.8, + tooltip: { + title: '开销', + }, + interactions: [{ name: 'radar-tooltip' }], + }); + + radar.render(); + expect(radar.chart).toBeDefined(); + expect(radar.chart.geometries.length).toBe(1); + expect(radar.chart.geometries[0].elements.length).toBe(1); + }); + + it('xField*yField*seriesField', () => { + const radar = new Radar(createDiv(), { + width: 400, + height: 300, + data: SERIES_DATA, + xField: 'name', + yField: 'value', + seriesField: 'type', + radius: 0.8, + tooltip: { + shared: false, + }, + interactions: [{ name: 'radar-tooltip' }], + }); + + radar.render(); + expect(radar.chart).toBeDefined(); + expect(radar.chart.geometries.length).toBe(1); + expect(radar.chart.geometries[0].elements.length).toBe(2); + expect(radar.chart.interactions['radar-tooltip']).toBeDefined(); + }); + + it('xField*yField*seriesField, with sharedTooltip', () => { + const radar = new Radar(createDiv(), { + width: 400, + height: 300, + data: SERIES_DATA, + xField: 'name', + yField: 'value', + seriesField: 'type', + radius: 0.8, + tooltip: { + showTitle: false, + shared: true, + itemTpl: `
  • + + + {name}:{title}{value} + +
  • `, + }, + interactions: [{ name: 'radar-tooltip' }], + }); + + radar.render(); + expect(radar.chart).toBeDefined(); + expect(radar.chart.geometries.length).toBe(1); + expect(radar.chart.geometries[0].elements.length).toBe(2); + }); + + it('"xy" crosshairs', () => { + const radar = new Radar(createDiv(), { + width: 400, + height: 300, + data: SERIES_DATA, + xField: 'name', + yField: 'value', + seriesField: 'type', + radius: 0.8, + tooltip: { + shared: true, + showCrosshairs: true, + crosshairs: { + type: 'xy', + line: { + style: { + stroke: '#565656', + lineDash: [4], + }, + }, + follow: true, + }, + }, + interactions: [ + { + name: 'radar-tooltip', + cfg: { + start: [{ trigger: 'plot:mousemove', action: 'radar-tooltip:show' }], + end: [{ trigger: 'plot:mouseleave', action: 'radar-tooltip:hide' }], + }, + }, + ], + }); + + radar.render(); + const tooltipController = radar.chart.getController('radar-tooltip'); + // @ts-ignore + const tooltipCfg = tooltipController.getTooltipCfg(); + expect(tooltipCfg.shared).toBe(true); + expect(tooltipCfg.showCrosshairs).toBe(true); + expect(tooltipCfg.crosshairs.type).toBe('xy'); + }); +}); diff --git a/src/plots/radar/index.ts b/src/plots/radar/index.ts index 0616a8821d..6a2b6298d9 100644 --- a/src/plots/radar/index.ts +++ b/src/plots/radar/index.ts @@ -2,6 +2,7 @@ import { Plot } from '../../core/plot'; import { Adaptor } from '../../core/adaptor'; import { RadarOptions } from './types'; import { adaptor } from './adaptor'; +import './interaction'; export { RadarOptions }; diff --git a/src/plots/radar/interaction/index.ts b/src/plots/radar/interaction/index.ts new file mode 100644 index 0000000000..86cdb02cdb --- /dev/null +++ b/src/plots/radar/interaction/index.ts @@ -0,0 +1,8 @@ +import { registerAction, registerInteraction } from '@antv/g2'; +import { RadarTooltipAction } from './radar-tooltip-action'; + +registerAction('radar-tooltip', RadarTooltipAction); +registerInteraction('radar-tooltip', { + start: [{ trigger: 'element:mouseenter', action: 'radar-tooltip:show' }], + end: [{ trigger: 'element:mouseleave', action: 'radar-tooltip:hide' }], +}); diff --git a/src/plots/radar/interaction/radar-tooltip-action.ts b/src/plots/radar/interaction/radar-tooltip-action.ts new file mode 100644 index 0000000000..c74ee7a010 --- /dev/null +++ b/src/plots/radar/interaction/radar-tooltip-action.ts @@ -0,0 +1,67 @@ +import { registerComponentController } from '@antv/g2'; +import { getTooltipItems } from '@antv/g2/lib/util/tooltip'; +import TooltipController from '@antv/g2/lib/chart/controller/tooltip'; +import { Action } from '@antv/g2/lib/interaction'; +import { isNil } from '@antv/util'; +import { Point } from '../../../types'; + +export class RadarTooltipController extends TooltipController { + public get name(): string { + return 'radar-tooltip'; + } + + public getTooltipItems(point: Point) { + const { shared, title: cfgTitle } = this.getTooltipCfg(); + const hintItems = super.getTooltipItems(point); + + if (hintItems.length > 0) { + const geometry = this.view.geometries[0]; + const dataArray = geometry.dataArray; + const title = hintItems[0].name; + const result = []; + dataArray.forEach((mappingData) => { + mappingData.forEach((d) => { + const items = getTooltipItems(d, geometry); + const item = items[0]; + if (!shared && item && item.name === title) { + const displayTitle = isNil(cfgTitle) ? title : cfgTitle; + result.push({ ...item, name: item.title, title: displayTitle }); + } else if (shared && item) { + const displayTitle = isNil(cfgTitle) ? item.name || title : cfgTitle; + result.push({ ...item, name: item.title, title: displayTitle }); + } + }); + }); + + return result; + } + return []; + } +} +registerComponentController('radar-tooltip', RadarTooltipController); + +/** + * 雷达图 tooltip 激活 action + */ +export class RadarTooltipAction extends Action { + init() { + const { view } = this.context; + view.removeInteraction('tooltip'); + } + + public show() { + const { event } = this.context; + const controller = this.getTooltipController(); + controller.showTooltip({ x: event.x, y: event.y }); + } + + public hide() { + const controller = this.getTooltipController(); + controller.hideTooltip(); + } + + private getTooltipController() { + const { view } = this.context; + return view.getController('radar-tooltip') as TooltipController; + } +} diff --git a/src/plots/radar/types.ts b/src/plots/radar/types.ts index df42de1837..5b41f15942 100644 --- a/src/plots/radar/types.ts +++ b/src/plots/radar/types.ts @@ -2,7 +2,9 @@ import { Options } from '../../types'; import { ShapeStyle } from '../../types/style'; export interface RadarOptions extends Options { + /** x 字段 */ readonly xField: string; + /** y 字段,映射雷达图的射线长度 */ readonly yField: string; /** 分组字段 */ readonly seriesField?: string;