diff --git a/src/plugins/highcharts/__stories__/UnsafeTooltip.stories.tsx b/src/plugins/highcharts/__stories__/UnsafeTooltip.stories.tsx new file mode 100644 index 00000000..e4d8fe53 --- /dev/null +++ b/src/plugins/highcharts/__stories__/UnsafeTooltip.stories.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import {Meta, Story} from '@storybook/react'; + +import {ChartKit} from '../../../components/ChartKit'; +import {data} from '../mocks/unsafe-tooltip'; + +import {ChartStory} from './components/ChartStory'; + +export default { + title: 'Plugins/Highcharts/UnsafeTooltip', + component: ChartKit, +} as Meta; + +const Template: Story = () => { + return ; +}; + +export const UnsafeTooltip = Template.bind({}); diff --git a/src/plugins/highcharts/mocks/unsafe-tooltip.ts b/src/plugins/highcharts/mocks/unsafe-tooltip.ts new file mode 100644 index 00000000..51699424 --- /dev/null +++ b/src/plugins/highcharts/mocks/unsafe-tooltip.ts @@ -0,0 +1,139 @@ +import type {HighchartsWidgetData} from '../types'; + +export const data: HighchartsWidgetData = { + data: { + graphs: [ + { + title: 'Profit', + tooltip: { + chartKitFormatting: true, + chartKitPrecision: 2, + unsafe: true, + }, + data: [ + { + y: 18451.2728, + dataLabels: { + enabled: false, + }, + label: '', + }, + { + y: 122490.80080000011, + dataLabels: { + enabled: false, + }, + label: '', + }, + { + y: 145454.9480999999, + dataLabels: { + enabled: false, + }, + label: '', + }, + ], + legendTitle: 'Profit', + colorKey: 'Profit', + colorGuid: null, + connectNulls: false, + yAxis: 0, + colorValue: 'Profit', + color: '#4DA2F1', + dashStyle: 'Solid', + name: 'Profit', + }, + { + title: 'Sales', + tooltip: { + chartKitFormatting: true, + chartKitPrecision: 2, + }, + data: [ + { + y: 741999.7952999998, + dataLabels: { + enabled: false, + }, + label: '', + }, + { + y: 719047.0320000029, + dataLabels: { + enabled: false, + }, + label: '', + }, + { + y: 836154.0329999966, + dataLabels: { + enabled: false, + }, + label: '', + }, + ], + legendTitle: 'Sales', + colorKey: 'Sales', + colorGuid: null, + connectNulls: false, + yAxis: 0, + colorValue: 'Sales', + color: '#FF3D64', + dashStyle: 'Solid', + name: 'Sales', + }, + ], + categories: ['Furniture', 'Office Supplies', 'Technology'], + }, + config: { + precision: 2, + hideHolidaysBands: true, + enableSum: true, + hideHolidays: false, + normalizeDiv: false, + normalizeSub: false, + manageTooltipConfig: (config) => { + config.lines.forEach((line, index) => { + line.commentText = `Some comment ${index + 1}`; + }); + + return config; + }, + unsafe: true, + }, + libraryConfig: { + chart: { + type: 'line', + zoomType: 'xy', + }, + legend: { + symbolWidth: 38, + }, + xAxis: { + endOnTick: false, + }, + yAxis: { + opposite: false, + labels: { + y: 3, + }, + type: 'linear', + }, + tooltip: {}, + plotOptions: { + series: { + dataGrouping: { + enabled: false, + }, + dataLabels: { + allowOverlap: false, + }, + }, + }, + axesFormatting: { + xAxis: [], + yAxis: [], + }, + enableSum: true, + }, +}; diff --git a/src/plugins/highcharts/renderer/helpers/tooltip/index.ts b/src/plugins/highcharts/renderer/helpers/tooltip/index.ts index 061f45e3..20b1cda2 100644 --- a/src/plugins/highcharts/renderer/helpers/tooltip/index.ts +++ b/src/plugins/highcharts/renderer/helpers/tooltip/index.ts @@ -14,7 +14,7 @@ import './tooltip.scss'; export const SERIES_NAME_DATA_ATTRIBUTE = 'data-series-name'; export const SERIES_IDX_DATA_ATTRIBUTE = 'data-series-idx'; export const TOOLTIP_CONTAINER_CLASS_NAME = '_tooltip'; -export const TOOLTIP_ROW_NAME_CLASS_NANE = '_tooltip-rows__name-td'; +export const TOOLTIP_ROW_NAME_CLASS_NAME = '_tooltip-rows__name-td'; export const TOOLTIP_ROW_CLASS_NAME = '_tooltip-row'; export const TOOLTIP_HEADER_CLASS_NAME = '_tooltip-header'; export const TOOLTIP_LIST_CLASS_NAME = '_tooltip-list'; @@ -35,10 +35,19 @@ const renderLineShapeCell = (line: TooltipLine) => `; -const renderNameCell = (line: TooltipLine) => - ` - ${line.hideSeriesName ? '' : escapeHTML(line.seriesName)} +const renderNameCell = (line: TooltipLine, options?: {unsafe?: boolean}) => { + let value = ''; + if (!line.hideSeriesName) { + value = line.seriesName; + + if (!options?.unsafe) { + value = escapeHTML(value); + } + } + return ` + ${value} `; +}; const renderPercentCell = (line: TooltipLine) => ` @@ -109,6 +118,7 @@ const renderRow = ( allowComment, withDarkBackground, rowIndex, + unsafe, }: RowRenderingConfig, ) => { const hasComment = line.commentText || line.xyCommentText; @@ -179,7 +189,7 @@ const renderRow = ( ? line.insertCellAt[index] : line.insertCellAt[index](line); } else { - return render(line); + return render(line, {unsafe}); } }) .join('')} @@ -245,6 +255,7 @@ export const formatTooltip = ( const rowRenderingConfig = { isSingleLine: lines.length === 1, cellsRenderers, + unsafe, }; const rowRenderingConfigForSelectedLine = { diff --git a/src/plugins/highcharts/renderer/helpers/tooltip/types.ts b/src/plugins/highcharts/renderer/helpers/tooltip/types.ts index 581f99bc..8cc35f6c 100644 --- a/src/plugins/highcharts/renderer/helpers/tooltip/types.ts +++ b/src/plugins/highcharts/renderer/helpers/tooltip/types.ts @@ -80,10 +80,11 @@ export type TooltipLine = { }; export type RowRenderingConfig = { - cellsRenderers: Array<(line: TooltipLine) => string>; + cellsRenderers: Array<(line: TooltipLine, options?: {unsafe?: boolean}) => string>; isSelectedLine?: boolean; allowComment?: boolean; withDarkBackground?: boolean; isSingleLine?: boolean; rowIndex?: number; + unsafe?: boolean; };