From 46ee7c2022c2d0d95e1c85bba1aa0f4803a0d110 Mon Sep 17 00:00:00 2001 From: MikaelNordberg Date: Tue, 25 Aug 2026 13:33:21 +0200 Subject: [PATCH 1/8] feat: Add tooltip type options to LineChart and enable chart feature in config --- .../components/Chart/LineChart/LineChart.tsx | 137 ++++++++++++++---- packages/pxweb2/public/config/config.js | 3 + .../components/Presentation/Presentation.tsx | 3 +- 3 files changed, 117 insertions(+), 26 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx index cd3b59b93..adb68a134 100644 --- a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx +++ b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx @@ -10,17 +10,23 @@ import { useEChartOption } from '../Utils/useEChartOption'; import type { PxTable } from '../../../shared-types/pxTable'; import { mapPxTableToChartDataset } from '../Utils/chartDataMapper'; import { getChartColorsFromCssVariables } from '../Utils/chartHelper'; +import { EChartsDataset } from '../Utils/chartTypes'; + +type TooltipType = 'axis' | 'item'; interface LineChartProps { readonly pxtable: PxTable; readonly colors?: string[]; + readonly tooltipType?: TooltipType; } type TooltipParam = { axisValueLabel?: string; + axisValue?: string | number; + name?: string; seriesIndex: number; seriesName: string; - data?: Record; + data?: Record; color?: string; }; @@ -41,29 +47,13 @@ function getTooltipSymbolSvg(symbol: string, color: string): string { } } -export function LineChart({ pxtable, colors }: LineChartProps) { - const dataset = useMemo(() => mapPxTableToChartDataset(pxtable), [pxtable]); - - const resolvedColors = useMemo(() => { - return colors && colors.length > 0 - ? colors - : getChartColorsFromCssVariables(); - }, [colors]); - - const option = useMemo( - () => ({ - ...buildDatasetOption(dataset), - grid: { top: 0, bottom: 200, left: '0', right: '0', containLabel: false }, - xAxis: { type: 'category' as const, axisLabel: { rotate: 45 } }, - yAxis: { - name: dataset.unit, - min: (value) => value.min, - }, - legend: { - height: 40 * dataset.series.length, // increase legend height based on number of series to prevent overlap with x-axis labels - }, - series: buildSeriesOption(dataset, 'line', resolvedColors), - tooltip: { +function getTooltip( + tooltipType: TooltipType, + dataset: EChartsDataset, +): echarts.EChartsOption['tooltip'] { + switch (tooltipType) { + case 'axis': + return { trigger: 'axis', formatter: (params: unknown) => { const axisParams = (Array.isArray(params) ? params : [params]) as @@ -91,9 +81,106 @@ export function LineChart({ pxtable, colors }: LineChartProps) { return `
${title}
${rows}
`; }, + }; + case 'item': + return { + trigger: 'item', + formatter: (params: unknown) => { + const param = params as TooltipParam; + const seriesMeta = dataset.series[param.seriesIndex]; + + if (!seriesMeta) { + return ''; + } + + const row = param.data; + const title = + param.axisValueLabel ?? + (param.axisValue != null ? String(param.axisValue) : undefined) ?? + param.name ?? + (row?.name != null ? String(row.name) : ''); + const value = row?.[seriesMeta.key] ?? ''; + const symbol = + LINE_SERIES_SYMBOLS[param.seriesIndex % LINE_SERIES_SYMBOLS.length]; + const color = param.color ?? '#666666'; + + return ` +
${title}
+
+ + ${getTooltipSymbolSvg(symbol, color)} + + ${param.seriesName}: ${value} +
+ `; + }, + }; + default: + return { + trigger: 'axis', + }; + } +} + +export function LineChart({ + pxtable, + colors, + tooltipType = 'axis', +}: LineChartProps) { + const dataset = useMemo(() => mapPxTableToChartDataset(pxtable), [pxtable]); + + const resolvedColors = useMemo(() => { + return colors && colors.length > 0 + ? colors + : getChartColorsFromCssVariables(); + }, [colors]); + + const option = useMemo( + () => ({ + ...buildDatasetOption(dataset), + grid: { top: 0, bottom: 200, left: '0', right: '0', containLabel: false }, + xAxis: { type: 'category' as const, axisLabel: { rotate: 45 } }, + yAxis: { + name: dataset.unit, + min: (value) => value.min, + }, + legend: { + height: 40 * dataset.series.length, // increase legend height based on number of series to prevent overlap with x-axis labels + }, + series: buildSeriesOption(dataset, 'line', resolvedColors), + tooltip: { + ...getTooltip(tooltipType, dataset), + + // trigger: 'axis', + // formatter: (params: unknown) => { + // const axisParams = (Array.isArray(params) ? params : [params]) as + // TooltipParam[] | undefined; + + // if (!axisParams || axisParams.length === 0) { + // return ''; + // } + + // const title = axisParams[0].axisValueLabel; + // const rows = axisParams + // .map((param) => { + // const seriesMeta = dataset.series[param.seriesIndex]; + // const row = param.data as Record; + // const value = row?.[seriesMeta.key]; + // const symbol = + // LINE_SERIES_SYMBOLS[ + // param.seriesIndex % LINE_SERIES_SYMBOLS.length + // ]; + // const color = param.color ?? '#666666'; + + // return `
${getTooltipSymbolSvg(symbol, color)}${param.seriesName}: ${value ?? ''}
`; + // }) + // .join(''); + + // return `
${title}
${rows}
`; + // }, }, }), - [dataset, resolvedColors], + [dataset, resolvedColors, tooltipType], ); const { divRef } = useEChartOption(option); diff --git a/packages/pxweb2/public/config/config.js b/packages/pxweb2/public/config/config.js index 87ec89910..26a9c5b36 100644 --- a/packages/pxweb2/public/config/config.js +++ b/packages/pxweb2/public/config/config.js @@ -38,4 +38,7 @@ globalThis.PxWeb2Config = { sv: '', // Set to your Swedish homepage URL en: '', // Set to your English homepage URL }, + features: { + chartEnabled: true, + }, }; diff --git a/packages/pxweb2/src/app/components/Presentation/Presentation.tsx b/packages/pxweb2/src/app/components/Presentation/Presentation.tsx index 1aa341565..f34092342 100644 --- a/packages/pxweb2/src/app/components/Presentation/Presentation.tsx +++ b/packages/pxweb2/src/app/components/Presentation/Presentation.tsx @@ -338,7 +338,8 @@ export function Presentation({ [classes.fadeChart]: isFadingTable, })} > - + + From 451a25b33b262ed0c728379ad0e0763f51d1e76d Mon Sep 17 00:00:00 2001 From: MikaelNordberg Date: Tue, 25 Aug 2026 15:17:33 +0200 Subject: [PATCH 2/8] feat: Enhance tooltip positioning and styling in LineChart component --- .../components/Chart/LineChart/LineChart.tsx | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx index adb68a134..214fc325d 100644 --- a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx +++ b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx @@ -30,6 +30,31 @@ type TooltipParam = { color?: string; }; +const getConfinedTooltipPosition: NonNullable< + echarts.TooltipComponentOption['position'] +> = (point, _params, _dom, _rect, size) => { + const padding = 8; + const offset = 12; + + const [rawX, rawY] = Array.isArray(point) + ? point + : [padding, padding]; + + const [contentWidth, contentHeight] = size?.contentSize ?? [0, 0]; + const [viewWidth, viewHeight] = size?.viewSize ?? [0, 0]; + + const desiredX = rawX + offset; + const desiredY = rawY + offset; + + const maxX = Math.max(padding, viewWidth - contentWidth - padding); + const maxY = Math.max(padding, viewHeight - contentHeight - padding); + + const x = Math.min(Math.max(desiredX, padding), maxX); + const y = Math.min(Math.max(desiredY, padding), maxY); + + return [x, y]; +}; + function getTooltipSymbolSvg(symbol: string, color: string): string { switch (symbol) { case 'rect': @@ -75,7 +100,7 @@ function getTooltip( ]; const color = param.color ?? '#666666'; - return `
${getTooltipSymbolSvg(symbol, color)}${param.seriesName}: ${value ?? ''}
`; + return `
${getTooltipSymbolSvg(symbol, color)}${param.seriesName}: ${value ?? ''}
`; }) .join(''); @@ -106,8 +131,8 @@ function getTooltip( return `
${title}
-
- +
+ ${getTooltipSymbolSvg(symbol, color)} ${param.seriesName}: ${value} @@ -150,6 +175,11 @@ export function LineChart({ series: buildSeriesOption(dataset, 'line', resolvedColors), tooltip: { ...getTooltip(tooltipType, dataset), + confine: true, + appendToBody: true, + extraCssText: + 'max-width:min(92vw,340px);white-space:normal;word-break:break-word;overflow-wrap:anywhere;', + position: getConfinedTooltipPosition, // trigger: 'axis', // formatter: (params: unknown) => { From fd72da6de1761ce5d50fd649d5746a674c2bcc9f Mon Sep 17 00:00:00 2001 From: MikaelNordberg Date: Tue, 25 Aug 2026 15:41:11 +0200 Subject: [PATCH 3/8] Prettier code --- .../src/lib/components/Chart/LineChart/LineChart.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx index 214fc325d..ac0949521 100644 --- a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx +++ b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx @@ -36,9 +36,7 @@ const getConfinedTooltipPosition: NonNullable< const padding = 8; const offset = 12; - const [rawX, rawY] = Array.isArray(point) - ? point - : [padding, padding]; + const [rawX, rawY] = Array.isArray(point) ? point : [padding, padding]; const [contentWidth, contentHeight] = size?.contentSize ?? [0, 0]; const [viewWidth, viewHeight] = size?.viewSize ?? [0, 0]; From c47ed294d8a69bf91d00edf3eacfa1491358ec05 Mon Sep 17 00:00:00 2001 From: MikaelNordberg Date: Tue, 25 Aug 2026 16:13:44 +0200 Subject: [PATCH 4/8] feat: Update LineChart to support 'item' tooltip type with enhanced symbol visibility --- .../components/Chart/LineChart/LineChart.tsx | 53 +++++++++---------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx index ac0949521..5141aead2 100644 --- a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx +++ b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx @@ -158,6 +158,28 @@ export function LineChart({ : getChartColorsFromCssVariables(); }, [colors]); + const series = useMemo(() => { + const baseSeries = buildSeriesOption(dataset, 'line', resolvedColors); + + if (tooltipType !== 'item') { + return baseSeries; + } + + // Show all symbols for 'item' tooltip type to ensure that the tooltip is displayed for all data points + return baseSeries.map((seriesOption) => { + if (seriesOption.type !== 'line') { + return seriesOption; + } + + return { + ...seriesOption, + showSymbol: true, + showAllSymbol: true, + symbolSize: 7, + } as echarts.LineSeriesOption; + }); + }, [dataset, resolvedColors, tooltipType]); + const option = useMemo( () => ({ ...buildDatasetOption(dataset), @@ -170,7 +192,7 @@ export function LineChart({ legend: { height: 40 * dataset.series.length, // increase legend height based on number of series to prevent overlap with x-axis labels }, - series: buildSeriesOption(dataset, 'line', resolvedColors), + series, tooltip: { ...getTooltip(tooltipType, dataset), confine: true, @@ -179,36 +201,9 @@ export function LineChart({ 'max-width:min(92vw,340px);white-space:normal;word-break:break-word;overflow-wrap:anywhere;', position: getConfinedTooltipPosition, - // trigger: 'axis', - // formatter: (params: unknown) => { - // const axisParams = (Array.isArray(params) ? params : [params]) as - // TooltipParam[] | undefined; - - // if (!axisParams || axisParams.length === 0) { - // return ''; - // } - - // const title = axisParams[0].axisValueLabel; - // const rows = axisParams - // .map((param) => { - // const seriesMeta = dataset.series[param.seriesIndex]; - // const row = param.data as Record; - // const value = row?.[seriesMeta.key]; - // const symbol = - // LINE_SERIES_SYMBOLS[ - // param.seriesIndex % LINE_SERIES_SYMBOLS.length - // ]; - // const color = param.color ?? '#666666'; - - // return `
${getTooltipSymbolSvg(symbol, color)}${param.seriesName}: ${value ?? ''}
`; - // }) - // .join(''); - - // return `
${title}
${rows}
`; - // }, }, }), - [dataset, resolvedColors, tooltipType], + [dataset, series, tooltipType], ); const { divRef } = useEChartOption(option); From e662960b3863404c24e5332562eb8d743d41a9f4 Mon Sep 17 00:00:00 2001 From: MikaelNordberg Date: Tue, 25 Aug 2026 16:16:28 +0200 Subject: [PATCH 5/8] Prettier code --- .../pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx index 5141aead2..0959b958a 100644 --- a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx +++ b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx @@ -200,7 +200,6 @@ export function LineChart({ extraCssText: 'max-width:min(92vw,340px);white-space:normal;word-break:break-word;overflow-wrap:anywhere;', position: getConfinedTooltipPosition, - }, }), [dataset, series, tooltipType], From 6ee30793d34ccdf3ba6f472e22ecdd56cafcccf5 Mon Sep 17 00:00:00 2001 From: MikaelNordberg Date: Tue, 25 Aug 2026 16:26:43 +0200 Subject: [PATCH 6/8] feat: Enhance tooltip display by bolding series values in LineChart component --- .../src/lib/components/Chart/LineChart/LineChart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx index 0959b958a..a93604934 100644 --- a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx +++ b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx @@ -98,7 +98,7 @@ function getTooltip( ]; const color = param.color ?? '#666666'; - return `
${getTooltipSymbolSvg(symbol, color)}${param.seriesName}: ${value ?? ''}
`; + return `
${getTooltipSymbolSvg(symbol, color)}${param.seriesName}: ${value ?? ''}
`; }) .join(''); @@ -133,7 +133,7 @@ function getTooltip( ${getTooltipSymbolSvg(symbol, color)} - ${param.seriesName}: ${value} + ${param.seriesName}: ${value}
`; }, From 6f22d7dbb80aed9fb20975981baaa8da832d96f6 Mon Sep 17 00:00:00 2001 From: MikaelNordberg Date: Tue, 25 Aug 2026 16:37:25 +0200 Subject: [PATCH 7/8] feat: Bold series values in tooltip for improved visibility in LineChart tests --- .../src/lib/components/Chart/LineChart/LineChart.spec.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.spec.tsx b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.spec.tsx index 94ad83347..8356601c3 100644 --- a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.spec.tsx +++ b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.spec.tsx @@ -191,8 +191,8 @@ describe('LineChart', () => { ]); expect(html).toContain('
2024
'); - expect(html).toContain('Men: 10'); - expect(html).toContain('Women: 12'); + expect(html).toContain('Men: 10'); + expect(html).toContain('Women: 12'); expect(html).toContain('fill="#666666"'); expect(html).toContain('fill="#ff0000"'); expect(html).toContain(' Date: Wed, 26 Aug 2026 13:46:40 +0200 Subject: [PATCH 8/8] feat: Add axis pointer to 'item' tooltip for enhanced data visibility in LineChart --- .../pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx index a93604934..067e83336 100644 --- a/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx +++ b/packages/pxweb2-ui/src/lib/components/Chart/LineChart/LineChart.tsx @@ -108,6 +108,9 @@ function getTooltip( case 'item': return { trigger: 'item', + axisPointer: { + type: 'cross', + }, formatter: (params: unknown) => { const param = params as TooltipParam; const seriesMeta = dataset.series[param.seriesIndex];