diff --git a/src/plugins/highcharts/renderer/helpers/config/config.js b/src/plugins/highcharts/renderer/helpers/config/config.js index 3e1dad5b..e2197e50 100644 --- a/src/plugins/highcharts/renderer/helpers/config/config.js +++ b/src/plugins/highcharts/renderer/helpers/config/config.js @@ -41,6 +41,7 @@ import { setNavigatorDefaultPeriod, numberFormat, getFormatOptionsFromLine, + checkTooltipPinningAvailability, } from './utils'; import {handleLegendItemClick} from './handleLegendItemClick'; import {getChartKitFormattedValue} from './utils/getChartKitFormattedValue'; @@ -1156,7 +1157,9 @@ export function hideFixedTooltip(tooltip, isMobile) { if (Array.isArray(tooltip.pointsOnFixedTooltip)) { tooltip.pointsOnFixedTooltip.forEach((point) => { - point.setState(''); + if (typeof point.setState === 'function') { + point.setState(''); + } }); } else { tooltip.pointsOnFixedTooltip.setState(''); @@ -1187,9 +1190,13 @@ export function hideFixedTooltip(tooltip, isMobile) { } function fixTooltip(tooltip, options, event) { - const pinable = get(options, 'tooltip.pinable', true); + const availableToPin = checkTooltipPinningAvailability({ + tooltip: options.tooltip, + altKey: event.altKey, + metaKey: event.metaKey, + }); - if (options.splitTooltip || !pinable) { + if (options.splitTooltip || (!availableToPin && !tooltip.fixed)) { return false; } diff --git a/src/plugins/highcharts/renderer/helpers/config/utils/index.ts b/src/plugins/highcharts/renderer/helpers/config/utils/index.ts index 8dec3df4..23a81045 100644 --- a/src/plugins/highcharts/renderer/helpers/config/utils/index.ts +++ b/src/plugins/highcharts/renderer/helpers/config/utils/index.ts @@ -5,7 +5,7 @@ export {concatStrings} from './concatStrings'; export {getChartKitFormattedValue} from './getChartKitFormattedValue'; export {getFormatOptionsFromLine} from './getFormatOptionsFromLine'; export {getXAxisThresholdValue} from './getXAxisThresholdValue'; -export {isTooltipShared} from './isTooltipShared'; +export * from './tooltip'; export {isNavigatorSeries} from './isNavigatorSeries'; export {isSafari} from './isSafari'; export {mergeArrayWithObject} from './mergeArrayWithObject'; diff --git a/src/plugins/highcharts/renderer/helpers/config/utils/isTooltipShared.test.ts b/src/plugins/highcharts/renderer/helpers/config/utils/isTooltipShared.test.ts deleted file mode 100644 index 816c5e4b..00000000 --- a/src/plugins/highcharts/renderer/helpers/config/utils/isTooltipShared.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {HighchartsType} from '../../constants'; -import {isTooltipShared} from './isTooltipShared'; - -const chartTypes: [HighchartsType, boolean][] = [ - [HighchartsType.Sankey, false], - [HighchartsType.Xrange, false], - [HighchartsType.Line, true], - [HighchartsType.Area, true], - [HighchartsType.Arearange, true], - [HighchartsType.Bar, true], - [HighchartsType.Column, true], - [HighchartsType.Columnrange, true], - [HighchartsType.Funnel, true], - [HighchartsType.Pie, true], - [HighchartsType.Map, true], - [HighchartsType.Scatter, true], - [HighchartsType.Bubble, true], - [HighchartsType.Heatmap, true], - [HighchartsType.Treemap, true], - [HighchartsType.Networkgraph, true], - [HighchartsType.Variwide, true], - [HighchartsType.Waterfall, true], - [HighchartsType.Streamgraph, true], - [HighchartsType.Wordcloud, true], - [HighchartsType.Boxplot, true], - [HighchartsType.Timeline, true], -]; - -describe('plugins/highcharts/config/calculatePrecision', () => { - test.each(chartTypes)(`for %s return %s`, (chartType, assert) => { - expect(isTooltipShared(chartType)).toBe(assert); - }); -}); diff --git a/src/plugins/highcharts/renderer/helpers/config/utils/isTooltipShared.ts b/src/plugins/highcharts/renderer/helpers/config/utils/isTooltipShared.ts deleted file mode 100644 index 15be7e29..00000000 --- a/src/plugins/highcharts/renderer/helpers/config/utils/isTooltipShared.ts +++ /dev/null @@ -1,9 +0,0 @@ -// In case of using 'sankey' or 'xrange', the shared property must be set to false, otherwise the tooltip behaves incorrectly: -// Point.onMouseOver -> Highcharts.Pointer.runPointActions -> H.Tooltip.refresh -> Cannot read property 'series' of undefined -export const isTooltipShared = (chartType: string) => { - if (['sankey', 'xrange'].includes(chartType)) { - return false; - } - - return true; -}; diff --git a/src/plugins/highcharts/renderer/helpers/config/utils/tooltip.test.ts b/src/plugins/highcharts/renderer/helpers/config/utils/tooltip.test.ts new file mode 100644 index 00000000..634cce29 --- /dev/null +++ b/src/plugins/highcharts/renderer/helpers/config/utils/tooltip.test.ts @@ -0,0 +1,48 @@ +import {HighchartsType} from '../../constants'; +import {checkTooltipPinningAvailability, isTooltipShared} from './tooltip'; + +const chartTypes: [HighchartsType, boolean][] = [ + [HighchartsType.Sankey, false], + [HighchartsType.Xrange, false], + [HighchartsType.Line, true], + [HighchartsType.Area, true], + [HighchartsType.Arearange, true], + [HighchartsType.Bar, true], + [HighchartsType.Column, true], + [HighchartsType.Columnrange, true], + [HighchartsType.Funnel, true], + [HighchartsType.Pie, true], + [HighchartsType.Map, true], + [HighchartsType.Scatter, true], + [HighchartsType.Bubble, true], + [HighchartsType.Heatmap, true], + [HighchartsType.Treemap, true], + [HighchartsType.Networkgraph, true], + [HighchartsType.Variwide, true], + [HighchartsType.Waterfall, true], + [HighchartsType.Streamgraph, true], + [HighchartsType.Wordcloud, true], + [HighchartsType.Boxplot, true], + [HighchartsType.Timeline, true], +]; + +describe('plugins/highcharts/config', () => { + test.each(chartTypes)(`calculatePrecision for %s return %s`, (chartType, expected) => { + expect(isTooltipShared(chartType)).toBe(expected); + }); + + test.each([ + [undefined, true], + [{tooltip: {pin: {altKey: true}}, altKey: true}, true], + [{tooltip: {pin: {metaKey: true}}, metaKey: true}, true], + [{tooltip: {pin: {altKey: true, metaKey: true}}, altKey: true, metaKey: true}, true], + [{tooltip: {pin: {enabled: false}}}, false], + [{tooltip: {pin: {altKey: true}}, altKey: false}, false], + [{tooltip: {pin: {metaKey: true}}, metaKey: false}, false], + [{tooltip: {pin: {altKey: true, metaKey: true}}, altKey: false, metaKey: true}, false], + [{tooltip: {pin: {altKey: true, metaKey: true}}, altKey: true, metaKey: false}, false], + ])(`checkTooltipPinningAvailability (args: %j)`, (args, expected) => { + const result = checkTooltipPinningAvailability(args); + expect(result).toBe(expected); + }); +}); diff --git a/src/plugins/highcharts/renderer/helpers/config/utils/tooltip.ts b/src/plugins/highcharts/renderer/helpers/config/utils/tooltip.ts new file mode 100644 index 00000000..0dae58e3 --- /dev/null +++ b/src/plugins/highcharts/renderer/helpers/config/utils/tooltip.ts @@ -0,0 +1,38 @@ +import {HighchartsWidgetData} from '../../../../types'; + +// In case of using 'sankey' or 'xrange', the shared property must be set to false, otherwise the tooltip behaves incorrectly: +// Point.onMouseOver -> Highcharts.Pointer.runPointActions -> H.Tooltip.refresh -> Cannot read property 'series' of undefined +export const isTooltipShared = (chartType: string) => { + if (['sankey', 'xrange'].includes(chartType)) { + return false; + } + + return true; +}; + +export const checkTooltipPinningAvailability = ( + args: { + tooltip?: HighchartsWidgetData['config']['tooltip']; + altKey?: boolean; + metaKey?: boolean; + } = {}, +) => { + const {tooltip, altKey, metaKey} = args; + const enabled = tooltip?.pin?.enabled ?? true; + const shouldAltKeyBePressed = tooltip?.pin?.altKey ?? false; + const shouldMetaKeyBePressed = tooltip?.pin?.metaKey ?? false; + + if (!enabled) { + return false; + } + + if (shouldAltKeyBePressed && !altKey) { + return false; + } + + if (shouldMetaKeyBePressed && !metaKey) { + return false; + } + + return true; +}; diff --git a/src/plugins/highcharts/types/widget.ts b/src/plugins/highcharts/types/widget.ts index 0505fb0c..bd0380c5 100644 --- a/src/plugins/highcharts/types/widget.ts +++ b/src/plugins/highcharts/types/widget.ts @@ -90,10 +90,19 @@ export type HighchartsWidgetData = { enableSum?: boolean; unsafe?: boolean; /** + * @experimental * Tooltip config */ tooltip?: { - pinable?: boolean; + /** Pin config */ + pin?: { + /** Enable tooltip`s pinning by click */ + enabled?: boolean; + /** Pin tooltip with pressed alt key */ + altKey?: boolean; + /** Pin tooltip with pressed meta key */ + metaKey?: boolean; + }; }; /** * Used to modify tooltip data