From ad2684b4fe7da685ad7b12eb951dc1c367cefa23 Mon Sep 17 00:00:00 2001 From: connono <36756846+connono@users.noreply.github.com> Date: Mon, 27 Jul 2020 21:05:45 +0800 Subject: [PATCH] V2 tiny tooltip (#1345) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(minichart): add tooltip * fix: stash代码合并错误 * fix: 代码复用 * fix: 增加注释,代码复用 Co-authored-by: yinshi.wyl --- __tests__/unit/plots/tiny-area/index-spec.ts | 77 +++++++++++++++++++ .../unit/plots/tiny-column/index-spec.ts | 77 +++++++++++++++++++ __tests__/unit/plots/tiny-line/index-spec.ts | 77 +++++++++++++++++++ src/plots/tiny-area/adaptor.ts | 32 +++++++- src/plots/tiny-area/types.ts | 3 + src/plots/tiny-column/adaptor.ts | 31 +++++++- src/plots/tiny-column/types.ts | 3 + src/plots/tiny-line/adaptor.ts | 41 +++++++--- src/plots/tiny-line/constants.ts | 13 ++++ src/plots/tiny-line/types.ts | 3 + src/types/tooltip.ts | 13 ++++ 11 files changed, 353 insertions(+), 17 deletions(-) create mode 100644 src/plots/tiny-line/constants.ts diff --git a/__tests__/unit/plots/tiny-area/index-spec.ts b/__tests__/unit/plots/tiny-area/index-spec.ts index 9c23e53891..7c45790349 100644 --- a/__tests__/unit/plots/tiny-area/index-spec.ts +++ b/__tests__/unit/plots/tiny-area/index-spec.ts @@ -1,3 +1,5 @@ +import { TooltipCfg } from '@antv/g2/lib/interface'; +import { GeometryTooltipOption } from '@antv/g2/lib/interface'; import { TinyArea } from '../../../../src'; import { partySupport } from '../../../data/party-support'; import { createDiv } from '../../../utils/dom'; @@ -100,4 +102,79 @@ describe('tiny-area', () => { expect(tinyArea.chart.geometries[1].elements[0].shape.attr('stroke')).toEqual('#123456'); expect(tinyArea.chart.geometries[1].elements[0].shape.attr('lineWidth')).toEqual(2); }); + + it('data with tooltip', () => { + const tinyArea = new TinyArea(createDiv(), { + width: 80, + height: 40, + data: partySupport + .filter((o) => o.type === 'FF') + .map((item) => { + return item.value; + }), + autoFit: false, + tooltip: true, + }); + + tinyArea.render(); + + const chart = tinyArea.chart; + const tooltipOption = chart.getOptions().tooltip as TooltipCfg; + expect(tooltipOption.showTitle).toBe(false); + expect(tooltipOption.shared).toBe(true); + expect(tooltipOption.showMarkers).toBe(false); + expect(tooltipOption.itemTpl).toBe('{value}'); + expect(tooltipOption.containerTpl).toBe('
'); + expect(tooltipOption.domStyles).toEqual({ + 'g2-tooltip': { + padding: '2px', + fontSize: '10px', + }, + }); + }); + + it('data with custom tooltip', () => { + const tinyArea = new TinyArea(createDiv(), { + width: 80, + height: 40, + data: partySupport + .filter((o) => o.type === 'FF') + .map((item) => { + return item.value; + }), + autoFit: false, + tooltip: { + showCrosshairs: true, + formatter: (x, y) => { + return `有${y / 1000}千`; + }, + position: 'bottom', + offset: 0, + domStyles: { + 'g2-tooltip': { + padding: '5px', + fontSize: '12px', + }, + }, + }, + }); + + tinyArea.render(); + + const chart = tinyArea.chart; + const tooltipOption = chart.getOptions().tooltip as TooltipCfg; + expect(tooltipOption.showCrosshairs).toBe(true); + expect(tooltipOption.position).toBe('bottom'); + expect(tooltipOption.offset).toBe(0); + expect(tooltipOption.domStyles).toEqual({ + 'g2-tooltip': { + padding: '5px', + fontSize: '12px', + }, + }); + const geometry = tinyArea.chart.geometries[0]; + const geometryTooltipOption = geometry.tooltipOption as GeometryTooltipOption; + expect(geometryTooltipOption.fields).toEqual(['x', 'y']); + expect(geometryTooltipOption.callback(1, '3000')).toEqual({ value: '有3千' }); + }); }); diff --git a/__tests__/unit/plots/tiny-column/index-spec.ts b/__tests__/unit/plots/tiny-column/index-spec.ts index fbea4d6c86..42e50ffe73 100644 --- a/__tests__/unit/plots/tiny-column/index-spec.ts +++ b/__tests__/unit/plots/tiny-column/index-spec.ts @@ -1,3 +1,5 @@ +import { TooltipCfg } from '@antv/g2/lib/interface'; +import { GeometryTooltipOption } from '@antv/g2/lib/interface'; import { TinyColumn } from '../../../../src'; import { partySupport } from '../../../data/party-support'; import { createDiv } from '../../../utils/dom'; @@ -73,4 +75,79 @@ describe('tiny-column', () => { expect(tinyColumn.chart.geometries[0].elements[0].shape.attr('fill')).toEqual('#444444'); expect(tinyColumn.chart.geometries[0].elements[1].shape.attr('fill')).toEqual('#222222'); }); + + it('data with tooltip', () => { + const tinyColumn = new TinyColumn(createDiv(), { + width: 80, + height: 40, + data: partySupport + .filter((o) => o.type === 'FF') + .map((item) => { + return item.value; + }), + autoFit: false, + tooltip: true, + }); + + tinyColumn.render(); + + const chart = tinyColumn.chart; + const tooltipOption = chart.getOptions().tooltip as TooltipCfg; + expect(tooltipOption.showTitle).toBe(false); + expect(tooltipOption.shared).toBe(true); + expect(tooltipOption.showMarkers).toBe(false); + expect(tooltipOption.itemTpl).toBe('{value}'); + expect(tooltipOption.containerTpl).toBe('
'); + expect(tooltipOption.domStyles).toEqual({ + 'g2-tooltip': { + padding: '2px', + fontSize: '10px', + }, + }); + }); + + it('data with custom tooltip', () => { + const tinyColumn = new TinyColumn(createDiv(), { + width: 80, + height: 40, + data: partySupport + .filter((o) => o.type === 'FF') + .map((item) => { + return item.value; + }), + autoFit: false, + tooltip: { + showCrosshairs: true, + formatter: (x, y) => { + return `有${y / 1000}千`; + }, + position: 'bottom', + offset: 0, + domStyles: { + 'g2-tooltip': { + padding: '5px', + fontSize: '12px', + }, + }, + }, + }); + + tinyColumn.render(); + + const chart = tinyColumn.chart; + const tooltipOption = chart.getOptions().tooltip as TooltipCfg; + expect(tooltipOption.showCrosshairs).toBe(true); + expect(tooltipOption.position).toBe('bottom'); + expect(tooltipOption.offset).toBe(0); + expect(tooltipOption.domStyles).toEqual({ + 'g2-tooltip': { + padding: '5px', + fontSize: '12px', + }, + }); + const geometry = tinyColumn.chart.geometries[0]; + const geometryTooltipOption = geometry.tooltipOption as GeometryTooltipOption; + expect(geometryTooltipOption.fields).toEqual(['x', 'y']); + expect(geometryTooltipOption.callback(1, '3000')).toEqual({ value: '有3千' }); + }); }); diff --git a/__tests__/unit/plots/tiny-line/index-spec.ts b/__tests__/unit/plots/tiny-line/index-spec.ts index f09ebc4240..a03bf94130 100644 --- a/__tests__/unit/plots/tiny-line/index-spec.ts +++ b/__tests__/unit/plots/tiny-line/index-spec.ts @@ -1,3 +1,5 @@ +import { TooltipCfg } from '@antv/g2/lib/interface'; +import { GeometryTooltipOption } from '@antv/g2/lib/interface'; import { TinyLine } from '../../../../src'; import { partySupport } from '../../../data/party-support'; import { createDiv } from '../../../utils/dom'; @@ -79,4 +81,79 @@ describe('tiny-line', () => { }); expect(tinyLine.chart.geometries[0].elements[0].shape.attr('lineDash')).toEqual([4, 4]); }); + + it('data with tooltip', () => { + const tinyLine = new TinyLine(createDiv(), { + width: 80, + height: 40, + data: partySupport + .filter((o) => o.type === 'FF') + .map((item) => { + return item.value; + }), + autoFit: false, + tooltip: true, + }); + + tinyLine.render(); + + const chart = tinyLine.chart; + const tooltipOption = chart.getOptions().tooltip as TooltipCfg; + expect(tooltipOption.showTitle).toBe(false); + expect(tooltipOption.shared).toBe(true); + expect(tooltipOption.showMarkers).toBe(false); + expect(tooltipOption.itemTpl).toBe('{value}'); + expect(tooltipOption.containerTpl).toBe('
'); + expect(tooltipOption.domStyles).toEqual({ + 'g2-tooltip': { + padding: '2px', + fontSize: '10px', + }, + }); + }); + + it('data with custom tooltip', () => { + const tinyLine = new TinyLine(createDiv(), { + width: 80, + height: 40, + data: partySupport + .filter((o) => o.type === 'FF') + .map((item) => { + return item.value; + }), + autoFit: false, + tooltip: { + showCrosshairs: true, + formatter: (x, y) => { + return `有${y / 1000}千`; + }, + position: 'bottom', + offset: 0, + domStyles: { + 'g2-tooltip': { + padding: '5px', + fontSize: '12px', + }, + }, + }, + }); + + tinyLine.render(); + + const chart = tinyLine.chart; + const tooltipOption = chart.getOptions().tooltip as TooltipCfg; + expect(tooltipOption.showCrosshairs).toBe(true); + expect(tooltipOption.position).toBe('bottom'); + expect(tooltipOption.offset).toBe(0); + expect(tooltipOption.domStyles).toEqual({ + 'g2-tooltip': { + padding: '5px', + fontSize: '12px', + }, + }); + const geometry = tinyLine.chart.geometries[0]; + const geometryTooltipOption = geometry.tooltipOption as GeometryTooltipOption; + expect(geometryTooltipOption.fields).toEqual(['x', 'y']); + expect(geometryTooltipOption.callback(1, '3000')).toEqual({ value: '有3千' }); + }); }); diff --git a/src/plots/tiny-area/adaptor.ts b/src/plots/tiny-area/adaptor.ts index ecfa54805e..e14c4fd57c 100644 --- a/src/plots/tiny-area/adaptor.ts +++ b/src/plots/tiny-area/adaptor.ts @@ -2,6 +2,7 @@ import { isFunction } from '@antv/util'; import { Params } from '../../core/adaptor'; import { flow } from '../../utils'; import { TinyAreaOptions } from './types'; +import { DEFAULT_TOOLTIP_OPTIONS } from '../tiny-line/constants'; /** * 字段 @@ -64,10 +65,33 @@ function legend(params: Params): Params { * tooltip 配置 * @param params */ -function tooltip(params: Params): Params { - const { chart } = params; - - chart.tooltip(false); +export function tooltip(params: Params): Params { + const { chart, options } = params; + const { tooltip = false } = options; + + if (tooltip) { + if (typeof tooltip === 'object') { + const { formatter, domStyles, position, offset, showCrosshairs } = tooltip; + chart.tooltip({ + ...DEFAULT_TOOLTIP_OPTIONS, + showCrosshairs, + domStyles, + position, + offset, + }); + chart.geometries.map((geometry) => { + geometry.tooltip('x*y', (x, y) => { + return { + value: formatter(x, y), + }; + }); + }); + } else { + chart.tooltip(DEFAULT_TOOLTIP_OPTIONS); + } + } else { + chart.tooltip(false); + } return params; } diff --git a/src/plots/tiny-area/types.ts b/src/plots/tiny-area/types.ts index ef25f005df..cac3f58189 100644 --- a/src/plots/tiny-area/types.ts +++ b/src/plots/tiny-area/types.ts @@ -1,5 +1,6 @@ import { ChartOptions } from '../../types'; import { ShapeStyle } from '../../types/style'; +import { TinyTooltipOption } from '../../types/tooltip'; /** mini 图的配置继承自 ChartOptions,因为很多的 G2 图形配置都不需要 */ export interface TinyAreaOptions extends ChartOptions { @@ -14,4 +15,6 @@ export interface TinyAreaOptions extends ChartOptions { readonly lineStyle?: ShapeStyle | ((x?: number, y?: number) => ShapeStyle); /** 面积填充图形样式 */ readonly areaStyle?: ShapeStyle | ((x?: number, y?: number) => ShapeStyle); + /** tooltip配置 */ + readonly tooltip?: boolean | TinyTooltipOption; } diff --git a/src/plots/tiny-column/adaptor.ts b/src/plots/tiny-column/adaptor.ts index 1f950bad73..79a9cab434 100644 --- a/src/plots/tiny-column/adaptor.ts +++ b/src/plots/tiny-column/adaptor.ts @@ -2,6 +2,7 @@ import { isFunction } from '@antv/util'; import { Params } from '../../core/adaptor'; import { flow } from '../../utils'; import { TinyColumnOptions } from './types'; +import { DEFAULT_TOOLTIP_OPTIONS } from '../tiny-line/constants'; /** * 字段 @@ -63,10 +64,32 @@ function legend(params: Params): Params { * tooltip 配置 * @param params */ -function tooltip(params: Params): Params { - const { chart } = params; - - chart.tooltip(false); +export function tooltip(params: Params): Params { + const { chart, options } = params; + const { tooltip = false } = options; + + if (tooltip) { + if (typeof tooltip === 'object') { + const { formatter, domStyles, position, offset, showCrosshairs } = tooltip; + chart.tooltip({ + ...DEFAULT_TOOLTIP_OPTIONS, + showCrosshairs, + domStyles, + position, + offset, + }); + const geometry = chart.geometries[0]; + geometry.tooltip('x*y', (x, y) => { + return { + value: formatter(x, y), + }; + }); + } else { + chart.tooltip(DEFAULT_TOOLTIP_OPTIONS); + } + } else { + chart.tooltip(false); + } return params; } diff --git a/src/plots/tiny-column/types.ts b/src/plots/tiny-column/types.ts index e3e08fe40c..11cdf0a02a 100644 --- a/src/plots/tiny-column/types.ts +++ b/src/plots/tiny-column/types.ts @@ -1,5 +1,6 @@ import { ChartOptions } from '../../types'; import { ShapeStyle } from '../../types/style'; +import { TinyTooltipOption } from '../../types/tooltip'; /** mini 图的配置继承自 ChartOptions,因为很多的 G2 图形配置都不需要 */ export interface TinyColumnOptions extends ChartOptions { @@ -10,4 +11,6 @@ export interface TinyColumnOptions extends ChartOptions { readonly meta?: Record; /** 迷你柱形图形样式 */ readonly columnStyle?: ShapeStyle | ((x?: number, y?: number) => ShapeStyle); + /** tooltip配置 */ + readonly tooltip?: boolean | TinyTooltipOption; } diff --git a/src/plots/tiny-line/adaptor.ts b/src/plots/tiny-line/adaptor.ts index 31c7d0475a..a0b4e3a764 100644 --- a/src/plots/tiny-line/adaptor.ts +++ b/src/plots/tiny-line/adaptor.ts @@ -1,7 +1,8 @@ +import { isFunction } from '@antv/util'; import { Params } from '../../core/adaptor'; import { flow } from '../../utils'; import { TinyLineOptions } from './types'; -import { isFunction } from '@antv/util'; +import { DEFAULT_TOOLTIP_OPTIONS } from './constants'; /** * 字段 @@ -48,25 +49,47 @@ function axis(params: Params): Params { } /** - * legend 配置 + * tooltip 配置 * @param params */ -function legend(params: Params): Params { - const { chart } = params; - - chart.legend(false); +export function tooltip(params: Params): Params { + const { chart, options } = params; + const { tooltip = false } = options; + + if (tooltip) { + if (typeof tooltip === 'object') { + const { formatter, domStyles, position, offset, showCrosshairs } = tooltip; + chart.tooltip({ + ...DEFAULT_TOOLTIP_OPTIONS, + showCrosshairs, + domStyles, + position, + offset, + }); + const geometry = chart.geometries[0]; + geometry.tooltip('x*y', (x, y) => { + return { + value: formatter(x, y), + }; + }); + } else { + chart.tooltip(DEFAULT_TOOLTIP_OPTIONS); + } + } else { + chart.tooltip(false); + } return params; } /** - * tooltip 配置 + * legend 配置 * @param params */ -function tooltip(params: Params): Params { +function legend(params: Params): Params { const { chart } = params; - chart.tooltip(false); + chart.legend(false); return params; } diff --git a/src/plots/tiny-line/constants.ts b/src/plots/tiny-line/constants.ts new file mode 100644 index 0000000000..6c571db747 --- /dev/null +++ b/src/plots/tiny-line/constants.ts @@ -0,0 +1,13 @@ +export const DEFAULT_TOOLTIP_OPTIONS = { + showTitle: false, + shared: true, + showMarkers: false, + containerTpl: '
', + itemTpl: '{value}', + domStyles: { + 'g2-tooltip': { + padding: '2px', + fontSize: '10px', + }, + }, +}; diff --git a/src/plots/tiny-line/types.ts b/src/plots/tiny-line/types.ts index 77c461fa1a..a60740c6e8 100644 --- a/src/plots/tiny-line/types.ts +++ b/src/plots/tiny-line/types.ts @@ -1,5 +1,6 @@ import { ChartOptions } from '../../types'; import { ShapeStyle } from '../../types/style'; +import { TinyTooltipOption } from '../../types/tooltip'; /** mini 图的配置继承自 ChartOptions,因为很多的 G2 图形配置都不需要 */ export interface TinyLineOptions extends ChartOptions { @@ -16,4 +17,6 @@ export interface TinyLineOptions extends ChartOptions { readonly connectNulls?: boolean; /** 折线extra图形样式 */ readonly lineStyle?: ShapeStyle | ((x?: number, y?: number) => ShapeStyle); + /** tooltip配置 */ + readonly tooltip?: boolean | TinyTooltipOption; } diff --git a/src/types/tooltip.ts b/src/types/tooltip.ts index c9cd81ee0c..11511f6668 100644 --- a/src/types/tooltip.ts +++ b/src/types/tooltip.ts @@ -1,3 +1,16 @@ import { TooltipOption } from '@antv/g2/lib/interface'; export type Tooltip = TooltipOption; + +export type TinyTooltipOption = { + /** tootip body模版语言 */ + readonly formatter?: (x: number, y: number) => string; + /** 获取tooltip内部dom节点覆写样式 */ + readonly domStyles?: object; + /** tooltip定位位置 */ + readonly position?: 'top' | 'bottom' | 'left' | 'right'; + /** tooltip偏移位置 */ + readonly offset?: number; + /** 是否显示交叉线 */ + readonly showCrosshairs?: boolean; +};