Skip to content

Commit 8e34901

Browse files
fix(charts): format bar tooltip values with each series' own value axis
Multi-axis bar charts now read the tooltip unit from the axis each series is plotted on, so a secondary axis no longer inherits the first axis's format. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S6aTRnkiu7PxYZPNPXYEMV
1 parent d736526 commit 8e34901

2 files changed

Lines changed: 33 additions & 11 deletions

File tree

‎apps/sim/lib/charts/theme.test.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ describe('compact bar tooltips', () => {
1717
'2026: —'
1818
)
1919
})
20+
it('formats each series with its own value axis', () => {
21+
const option = applyChartTooltipDefaults({
22+
xAxis: { type: 'category' },
23+
yAxis: [{ type: 'value', axisLabel: { formatter: '{value}%' } }, { type: 'value' }],
24+
series: [
25+
{ type: 'bar', name: 'Rate' },
26+
{ type: 'bar', name: 'Count', yAxisIndex: 1 },
27+
],
28+
})
29+
const formatter = toRecord(option.tooltip).formatter
30+
if (typeof formatter !== 'function') throw new Error('Expected the bar tooltip formatter')
31+
expect(
32+
formatter([
33+
{ seriesIndex: 0, seriesName: 'Rate', axisValueLabel: 'Mon', value: 75 },
34+
{ seriesIndex: 1, seriesName: 'Count', axisValueLabel: 'Mon', value: 75 },
35+
])
36+
).toBe('Mon · Rate: 75%\nMon · Count: 75')
37+
})
2038
it('uses a floating tooltip with row hover and preserves authored overrides', () => {
2139
expect(applyChartTooltipDefaults({ series: [{ type: 'bar' }] }).tooltip).toMatchObject({
2240
trigger: 'axis',

‎apps/sim/lib/charts/theme.ts‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function encodedTooltipValue(entry: Record<string, unknown>, dimension: 'x' | 'y
1919
export function formatBarTooltip(
2020
params: unknown,
2121
valueAxis: 'x' | 'y' = 'y',
22-
valueFormatter?: string
22+
valueFormatter?: string | ((seriesIndex: number) => string | undefined)
2323
): string {
2424
const entries = (Array.isArray(params) ? params : [params]).filter(isRecordLike)
2525
return entries
@@ -31,7 +31,11 @@ export function formatBarTooltip(
3131
? entry.seriesName
3232
: ''
3333
const label = entries.length > 1 && name ? `${category} · ${name}` : category || name
34-
return `${label}: ${formatChartValue(value, valueFormatter)}`
34+
const formatter =
35+
typeof valueFormatter === 'function'
36+
? valueFormatter(Number(entry.seriesIndex ?? 0))
37+
: valueFormatter
38+
return `${label}: ${formatChartValue(value, formatter)}`
3539
})
3640
.join('\n')
3741
}
@@ -54,19 +58,19 @@ export function applyChartTooltipDefaults(option: Record<string, unknown>) {
5458
if (series.length && series.every((entry) => toRecord(entry).type === 'bar')) {
5559
const yAxis = toRecord(Array.isArray(option.yAxis) ? option.yAxis[0] : option.yAxis)
5660
const valueAxisName = yAxis.type === 'category' ? 'x' : 'y'
57-
const valueAxis = option[`${valueAxisName}Axis`]
58-
const formatter = toRecord(
59-
toRecord(Array.isArray(valueAxis) ? valueAxis[0] : valueAxis).axisLabel
60-
).formatter
61+
const valueAxes = option[`${valueAxisName}Axis`]
62+
/** Each series reads units from its own value axis, so a secondary axis keeps its format. */
63+
const formatters = series.map((entry) => {
64+
const index = Number(toRecord(entry)[`${valueAxisName}AxisIndex`] ?? 0)
65+
const axis = toRecord(Array.isArray(valueAxes) ? valueAxes[index] : valueAxes)
66+
const formatter = toRecord(axis.axisLabel).formatter
67+
return typeof formatter === 'string' ? formatter : undefined
68+
})
6169
option.tooltip = mapTooltipEntries(option.tooltip, (tooltip) => ({
6270
trigger: 'axis',
6371
showContent: true,
6472
formatter: (params: unknown) =>
65-
formatBarTooltip(
66-
params,
67-
valueAxisName,
68-
typeof formatter === 'string' ? formatter : undefined
69-
),
73+
formatBarTooltip(params, valueAxisName, (seriesIndex) => formatters[seriesIndex]),
7074
...tooltip,
7175
axisPointer: { type: 'shadow', ...toRecord(tooltip.axisPointer) },
7276
}))

0 commit comments

Comments
 (0)