basalt-ui 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +38 -2
- package/agent/rules/basalt-charts.md +279 -108
- package/agent/rules/basalt-mantine.md +4 -2
- package/agent/skills/basalt-charts/SKILL.md +114 -64
- package/agent/skills/basalt-design/SKILL.md +4 -3
- package/agent/templates/CLAUDE-block.md.tpl +9 -6
- package/configs/oxlint-plugin.js +152 -0
- package/configs/oxlint-plugin.test.ts +107 -0
- package/configs/oxlint.json +2 -0
- package/dist/charts/cursor/resolve.d.ts +33 -0
- package/dist/charts/cursor/resolve.d.ts.map +1 -0
- package/dist/charts/cursor/resolve.js +61 -0
- package/dist/charts/cursor/resolve.test.d.ts +2 -0
- package/dist/charts/cursor/resolve.test.d.ts.map +1 -0
- package/dist/charts/cursor/scope.d.ts +17 -0
- package/dist/charts/cursor/scope.d.ts.map +1 -0
- package/dist/charts/cursor/scope.js +20 -0
- package/dist/charts/cursor/store.d.ts +31 -0
- package/dist/charts/cursor/store.d.ts.map +1 -0
- package/dist/charts/cursor/store.js +25 -0
- package/dist/charts/cursor/store.test.d.ts +2 -0
- package/dist/charts/cursor/store.test.d.ts.map +1 -0
- package/dist/charts/hooks/useChartCursor.d.ts +39 -0
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -0
- package/dist/charts/hooks/useChartCursor.js +104 -0
- package/dist/charts/hooks/useChartSize.d.ts +8 -4
- package/dist/charts/hooks/useChartSize.d.ts.map +1 -1
- package/dist/charts/index.d.ts +16 -13
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +38 -19
- package/dist/charts/kinds/Bars.d.ts +45 -54
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +235 -392
- package/dist/charts/kinds/Bars.test.d.ts +2 -0
- package/dist/charts/kinds/Bars.test.d.ts.map +1 -0
- package/dist/charts/kinds/Donut.d.ts +5 -4
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +17 -14
- package/dist/charts/kinds/DualPanel.d.ts +13 -0
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +185 -92
- package/dist/charts/kinds/DualPanel.test.d.ts +2 -0
- package/dist/charts/kinds/DualPanel.test.d.ts.map +1 -0
- package/dist/charts/kinds/Heatmap.d.ts +15 -11
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +38 -22
- package/dist/charts/kinds/MultiLine.d.ts +27 -35
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +122 -235
- package/dist/charts/kinds/MultiLine.test.d.ts +2 -0
- package/dist/charts/kinds/MultiLine.test.d.ts.map +1 -0
- package/dist/charts/kinds/StackedArea.d.ts +21 -10
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +74 -132
- package/dist/charts/kinds/ZonedLine.d.ts +24 -40
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +104 -184
- package/dist/charts/kinds/ZonedLine.test.d.ts +2 -0
- package/dist/charts/kinds/ZonedLine.test.d.ts.map +1 -0
- package/dist/charts/layout/auto-margin.d.ts +60 -0
- package/dist/charts/layout/auto-margin.d.ts.map +1 -0
- package/dist/charts/layout/auto-margin.js +37 -0
- package/dist/charts/layout/auto-margin.test.d.ts +2 -0
- package/dist/charts/layout/auto-margin.test.d.ts.map +1 -0
- package/dist/charts/primitives/CartesianChart.d.ts +161 -0
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -0
- package/dist/charts/primitives/CartesianChart.js +359 -0
- package/dist/charts/primitives/CartesianChart.test.d.ts +2 -0
- package/dist/charts/primitives/CartesianChart.test.d.ts.map +1 -0
- package/dist/charts/primitives/ChartFrame.d.ts +16 -5
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +17 -2
- package/dist/charts/primitives/ChartLegend.d.ts +15 -1
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +25 -3
- package/dist/charts/primitives/ChartLegend.test.d.ts +2 -0
- package/dist/charts/primitives/ChartLegend.test.d.ts.map +1 -0
- package/dist/charts/primitives/ChartTooltip.d.ts +16 -16
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +45 -19
- package/dist/charts/primitives/HoverOverlay.d.ts +19 -4
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +22 -1
- package/dist/charts/primitives/XZoneRects.d.ts +54 -0
- package/dist/charts/primitives/XZoneRects.d.ts.map +1 -0
- package/dist/charts/primitives/XZoneRects.js +65 -0
- package/dist/charts/primitives/XZoneRects.test.d.ts +2 -0
- package/dist/charts/primitives/XZoneRects.test.d.ts.map +1 -0
- package/dist/charts/series.d.ts +28 -3
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/series.js +5 -2
- package/dist/charts/series.test.d.ts +2 -0
- package/dist/charts/series.test.d.ts.map +1 -0
- package/dist/charts/utils/measure-text.d.ts +25 -0
- package/dist/charts/utils/measure-text.d.ts.map +1 -0
- package/dist/charts/utils/measure-text.js +64 -0
- package/dist/charts/utils/measure-text.test.d.ts +2 -0
- package/dist/charts/utils/measure-text.test.d.ts.map +1 -0
- package/dist/content/fence-block.d.ts.map +1 -1
- package/dist/surfaces.d.ts +1 -1
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +1 -1
- package/dist/tokens/chart-margin.test.d.ts +2 -0
- package/dist/tokens/chart-margin.test.d.ts.map +1 -0
- package/dist/tokens/index.d.ts +29 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +10 -0
- package/dist/tokens/palette.d.ts +4 -0
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +3 -0
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/charts/cursor/resolve.test.ts +61 -0
- package/src/charts/cursor/resolve.ts +102 -0
- package/src/charts/cursor/scope.tsx +32 -0
- package/src/charts/cursor/store.test.ts +46 -0
- package/src/charts/cursor/store.ts +53 -0
- package/src/charts/hooks/useChartCursor.ts +163 -0
- package/src/charts/hooks/useChartSize.ts +8 -4
- package/src/charts/index.ts +35 -14
- package/src/charts/kinds/Bars.test.tsx +98 -0
- package/src/charts/kinds/Bars.tsx +350 -467
- package/src/charts/kinds/Donut.tsx +31 -23
- package/src/charts/kinds/DualPanel.test.tsx +131 -0
- package/src/charts/kinds/DualPanel.tsx +301 -135
- package/src/charts/kinds/Heatmap.tsx +72 -47
- package/src/charts/kinds/MultiLine.test.tsx +37 -0
- package/src/charts/kinds/MultiLine.tsx +171 -290
- package/src/charts/kinds/StackedArea.tsx +116 -175
- package/src/charts/kinds/ZonedLine.test.tsx +37 -0
- package/src/charts/kinds/ZonedLine.tsx +149 -255
- package/src/charts/layout/auto-margin.test.ts +42 -0
- package/src/charts/layout/auto-margin.ts +110 -0
- package/src/charts/primitives/CartesianChart.test.tsx +160 -0
- package/src/charts/primitives/CartesianChart.tsx +598 -0
- package/src/charts/primitives/ChartFrame.test.tsx +33 -0
- package/src/charts/primitives/ChartFrame.tsx +40 -4
- package/src/charts/primitives/ChartLegend.test.tsx +78 -0
- package/src/charts/primitives/ChartLegend.tsx +52 -2
- package/src/charts/primitives/ChartTooltip.tsx +67 -37
- package/src/charts/primitives/HoverOverlay.tsx +39 -3
- package/src/charts/primitives/XZoneRects.test.tsx +123 -0
- package/src/charts/primitives/XZoneRects.tsx +132 -0
- package/src/charts/series.test.ts +112 -0
- package/src/charts/series.ts +33 -5
- package/src/charts/utils/measure-text.test.ts +36 -0
- package/src/charts/utils/measure-text.ts +109 -0
- package/src/content/fence-block.tsx +2 -3
- package/src/surfaces.ts +2 -1
- package/src/theme/spacing.test.ts +2 -0
- package/src/tokens/chart-margin.test.ts +39 -0
- package/src/tokens/density.test.ts +1 -0
- package/src/tokens/index.ts +36 -0
- package/src/tokens/palette.ts +3 -0
- package/dist/charts/hooks/useChartTooltip.d.ts +0 -13
- package/dist/charts/hooks/useChartTooltip.d.ts.map +0 -1
- package/dist/charts/hooks/useChartTooltip.js +0 -26
- package/dist/charts/hooks/useHoverSync.d.ts +0 -46
- package/dist/charts/hooks/useHoverSync.d.ts.map +0 -1
- package/dist/charts/hooks/useHoverSync.js +0 -75
- package/dist/charts/hover-context.d.ts +0 -21
- package/dist/charts/hover-context.d.ts.map +0 -1
- package/dist/charts/hover-context.js +0 -12
- package/dist/charts/hover-sync.d.ts +0 -14
- package/dist/charts/hover-sync.d.ts.map +0 -1
- package/dist/charts/hover-sync.js +0 -17
- package/dist/charts/primitives/ResponsiveChart.d.ts +0 -45
- package/dist/charts/primitives/ResponsiveChart.d.ts.map +0 -1
- package/dist/charts/primitives/ResponsiveChart.js +0 -24
- package/src/charts/hooks/useChartTooltip.ts +0 -31
- package/src/charts/hooks/useHoverSync.ts +0 -152
- package/src/charts/hover-context.ts +0 -28
- package/src/charts/hover-sync.tsx +0 -37
- package/src/charts/primitives/ResponsiveChart.tsx +0 -64
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Group } from '@visx/group'
|
|
2
2
|
import { Pie } from '@visx/shape'
|
|
3
|
-
import { memo, useMemo } from 'react'
|
|
4
|
-
import type { ReactNode } from 'react'
|
|
5
|
-
import {
|
|
3
|
+
import { memo, useMemo, useState } from 'react'
|
|
4
|
+
import type { PointerEvent, ReactNode } from 'react'
|
|
5
|
+
import { ChartTooltipFloat, TooltipBody, TooltipRow } from '../primitives/ChartTooltip'
|
|
6
6
|
import { ChartFrame } from '../primitives/ChartFrame'
|
|
7
|
-
import { useChartTooltip } from '../hooks/useChartTooltip'
|
|
8
7
|
import { VX } from '../../tokens'
|
|
9
8
|
import type { SeriesStyle } from '../series'
|
|
10
9
|
import type { SeriesKey } from '../../register'
|
|
@@ -60,10 +59,11 @@ export type DonutProps<K extends string = SeriesKey> = {
|
|
|
60
59
|
/**
|
|
61
60
|
* Radial slice-share chart with a punched-out center label. Composes `ChartFrame` for a
|
|
62
61
|
* categorical legend derived from the slices (one `SeriesStyle` per slice, `mark: 'bar'`) so the
|
|
63
|
-
* legend can never drift from what's plotted
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
62
|
+
* legend can never drift from what's plotted — legend-hidden slices drop out of the ring, the
|
|
63
|
+
* center total, and the tooltip's "Share" row together (`docs/CHARTS-SPEC.md` §5). No crosshair —
|
|
64
|
+
* meaningless for a radial layout. Hover stays local to the pie (dimming siblings on hover) rather
|
|
65
|
+
* than joining the shared cursor: a date-keyed cursor has no counterpart on a donut, and cross-kind
|
|
66
|
+
* category sync (donut ↔ bar, via a generalized key) is a distinct, deliberately deferred feature.
|
|
67
67
|
*/
|
|
68
68
|
function DonutInner<K extends string = SeriesKey>(props: DonutProps<K>) {
|
|
69
69
|
const { data, height, colorForKey, seriesLabel = (k) => k, ariaLabel, isPending } = props
|
|
@@ -88,9 +88,12 @@ function DonutInner<K extends string = SeriesKey>(props: DonutProps<K>) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
type DonutPlotProps<K extends string = SeriesKey> = DonutProps<K> & {
|
|
91
|
-
plot: { width: number; height: number }
|
|
91
|
+
plot: { width: number; height: number; hidden: ReadonlySet<string> }
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/** A hovered slice plus the viewport anchor `ChartTooltipFloat` positions against. */
|
|
95
|
+
type DonutTip<K extends string> = { key: K; value: number; anchor: { x: number; y: number } }
|
|
96
|
+
|
|
94
97
|
/** The measured plot — split from {@link DonutInner} so it only draws once `ChartFrame` has
|
|
95
98
|
* resolved a non-empty plot rect (radius/center depend on the measured size). */
|
|
96
99
|
function DonutPlot<K extends string = SeriesKey>(props: DonutPlotProps<K>) {
|
|
@@ -106,26 +109,31 @@ function DonutPlot<K extends string = SeriesKey>(props: DonutPlotProps<K>) {
|
|
|
106
109
|
innerRatio = 0.6,
|
|
107
110
|
padAngle = 0.01,
|
|
108
111
|
} = props
|
|
109
|
-
const { width, height } = plot
|
|
112
|
+
const { width, height, hidden } = plot
|
|
110
113
|
|
|
111
|
-
const
|
|
114
|
+
const [tip, setTip] = useState<DonutTip<K> | null>(null)
|
|
115
|
+
const hoveredKey = tip?.key ?? null
|
|
112
116
|
|
|
113
|
-
const
|
|
114
|
-
const hoveredKey = tip?.data.key ?? null
|
|
117
|
+
const visibleData = useMemo(() => data.filter((d) => !hidden.has(d.key)), [data, hidden])
|
|
115
118
|
|
|
116
119
|
const radius = Math.min(width, height) / 2 - 4
|
|
117
120
|
const innerRadius = radius * innerRatio
|
|
118
121
|
const centerX = width / 2
|
|
119
122
|
const centerY = height / 2
|
|
120
123
|
|
|
121
|
-
const total = useMemo(() =>
|
|
124
|
+
const total = useMemo(() => visibleData.reduce((sum, d) => sum + d.value, 0), [visibleData])
|
|
125
|
+
|
|
126
|
+
const show = (d: DonutDatum<K>, event: PointerEvent<SVGGElement>) => {
|
|
127
|
+
setTip({ key: d.key, value: d.value, anchor: { x: event.clientX, y: event.clientY } })
|
|
128
|
+
}
|
|
129
|
+
const hide = () => setTip(null)
|
|
122
130
|
|
|
123
131
|
return (
|
|
124
132
|
<div style={{ position: 'relative' }}>
|
|
125
133
|
<svg width={width} height={height}>
|
|
126
134
|
<Group left={centerX} top={centerY}>
|
|
127
135
|
<Pie<DonutDatum<K>>
|
|
128
|
-
data={
|
|
136
|
+
data={visibleData}
|
|
129
137
|
pieValue={(d) => d.value}
|
|
130
138
|
pieSortValues={() => 0}
|
|
131
139
|
outerRadius={radius}
|
|
@@ -210,24 +218,24 @@ function DonutPlot<K extends string = SeriesKey>(props: DonutPlotProps<K>) {
|
|
|
210
218
|
</div>
|
|
211
219
|
)}
|
|
212
220
|
|
|
213
|
-
|
|
214
|
-
{tip
|
|
221
|
+
{tip !== null && (
|
|
222
|
+
<ChartTooltipFloat anchor={tip.anchor}>
|
|
215
223
|
<TooltipBody>
|
|
216
224
|
<TooltipRow
|
|
217
|
-
color={colorForKey(tip.
|
|
218
|
-
label={seriesLabel(tip.
|
|
219
|
-
value={formatValue(tip.
|
|
225
|
+
color={colorForKey(tip.key)}
|
|
226
|
+
label={seriesLabel(tip.key)}
|
|
227
|
+
value={formatValue(tip.value)}
|
|
220
228
|
shape="bar"
|
|
221
229
|
/>
|
|
222
230
|
<TooltipRow
|
|
223
231
|
color={VX.grid}
|
|
224
232
|
label="Share"
|
|
225
|
-
value={`${total > 0 ? Math.round((tip.
|
|
233
|
+
value={`${total > 0 ? Math.round((tip.value / total) * 100) : 0}%`}
|
|
226
234
|
shape="bar"
|
|
227
235
|
/>
|
|
228
236
|
</TooltipBody>
|
|
229
|
-
|
|
230
|
-
|
|
237
|
+
</ChartTooltipFloat>
|
|
238
|
+
)}
|
|
231
239
|
</div>
|
|
232
240
|
)
|
|
233
241
|
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `DualPanel` — the bottom-pane domain seam (`bottomYDomain`/`bottomMaxAbsFloor`) and `formatBar`
|
|
3
|
+
* (tooltip-row-only override, `formatBottom` keeps owning the ticks). Same SSR harness as
|
|
4
|
+
* `ChartFrame.test.tsx` for the axis-only assertions (no hover needed); the `formatBar` case needs
|
|
5
|
+
* a live cursor, so it uses the real DOM harness + the overlay's keyboard path (parity with
|
|
6
|
+
* `Bars.test.tsx`) — DualPanel has two `HoverOverlay`s sharing one cursor, but only the TOP one is
|
|
7
|
+
* keyboard-focusable, so `getByRole('slider')` is unambiguous.
|
|
8
|
+
*/
|
|
9
|
+
import { fireEvent, render, screen } from '@testing-library/react'
|
|
10
|
+
import { describe, expect, test } from 'bun:test'
|
|
11
|
+
import { renderToStaticMarkup } from 'react-dom/server'
|
|
12
|
+
import { DualPanel } from './DualPanel'
|
|
13
|
+
import type { ChartSeries } from '../series'
|
|
14
|
+
|
|
15
|
+
type Row = { date: string; v: number; bar: number }
|
|
16
|
+
|
|
17
|
+
const rows: Row[] = [{ date: '2026-08-01', v: 10, bar: 0.02 }]
|
|
18
|
+
|
|
19
|
+
const series: ChartSeries<Row>[] = [
|
|
20
|
+
{ key: 'v', label: 'V', color: '#111', mark: 'line', getValue: (d) => d.v },
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
function renderStatic(bottomMaxAbsFloor?: number, bottomYDomain?: [number, number]): string {
|
|
24
|
+
return renderToStaticMarkup(
|
|
25
|
+
<DualPanel<Row>
|
|
26
|
+
data={rows}
|
|
27
|
+
chartId="dp-floor"
|
|
28
|
+
getX={(d) => d.date}
|
|
29
|
+
series={series}
|
|
30
|
+
getBar={(d) => d.bar}
|
|
31
|
+
barLabel="Bar"
|
|
32
|
+
barColorPositive="#0a0"
|
|
33
|
+
barColorNegative="#a00"
|
|
34
|
+
formatTop={(v) => String(v)}
|
|
35
|
+
formatBottom={(v) => v.toFixed(2)}
|
|
36
|
+
{...(bottomMaxAbsFloor !== undefined && { bottomMaxAbsFloor })}
|
|
37
|
+
{...(bottomYDomain !== undefined && { bottomYDomain })}
|
|
38
|
+
/>,
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('DualPanel — bottomMaxAbsFloor', () => {
|
|
43
|
+
test('floors the symmetric max-abs so a near-zero plateau does not amplify to full height', () => {
|
|
44
|
+
// The raw data's max abs is 0.02 — nowhere near the 0.5 floor, so "0.50" can only appear on
|
|
45
|
+
// the bottom axis once the floor widens the domain.
|
|
46
|
+
expect(renderStatic()).not.toContain('>0.50<')
|
|
47
|
+
expect(renderStatic(0.5)).toContain('>0.50<')
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('DualPanel — per-point getMarker rendering', () => {
|
|
52
|
+
test('renders exactly one circle per non-null marker, honouring color/r overrides', () => {
|
|
53
|
+
const markerRows: Row[] = [
|
|
54
|
+
{ date: '2026-08-01', v: 10, bar: 0.02 },
|
|
55
|
+
{ date: '2026-08-02', v: 12, bar: 0.03 },
|
|
56
|
+
{ date: '2026-08-03', v: 9, bar: 0.01 },
|
|
57
|
+
]
|
|
58
|
+
const markerSeries: ChartSeries<Row>[] = [
|
|
59
|
+
{
|
|
60
|
+
key: 'v',
|
|
61
|
+
label: 'V',
|
|
62
|
+
color: '#111',
|
|
63
|
+
mark: 'line',
|
|
64
|
+
getValue: (d) => d.v,
|
|
65
|
+
getMarker: (d) =>
|
|
66
|
+
d.date === '2026-08-01'
|
|
67
|
+
? { color: '#f00', r: 9 }
|
|
68
|
+
: d.date === '2026-08-03'
|
|
69
|
+
? { r: 3 }
|
|
70
|
+
: null,
|
|
71
|
+
},
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
const { container } = render(
|
|
75
|
+
<DualPanel<Row>
|
|
76
|
+
data={markerRows}
|
|
77
|
+
chartId="dp-markers"
|
|
78
|
+
getX={(d) => d.date}
|
|
79
|
+
series={markerSeries}
|
|
80
|
+
getBar={(d) => d.bar}
|
|
81
|
+
barLabel="Bar"
|
|
82
|
+
barColorPositive="#0a0"
|
|
83
|
+
barColorNegative="#a00"
|
|
84
|
+
formatTop={(v) => String(v)}
|
|
85
|
+
formatBottom={(v) => v.toFixed(2)}
|
|
86
|
+
/>,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const circles = container.querySelectorAll('circle')
|
|
90
|
+
expect(circles).toHaveLength(2)
|
|
91
|
+
expect(circles[0]?.getAttribute('fill')).toBe('#f00')
|
|
92
|
+
expect(circles[0]?.getAttribute('r')).toBe('9')
|
|
93
|
+
// No color override on the second marker — falls back to the series color.
|
|
94
|
+
expect(circles[1]?.getAttribute('fill')).toBe('#111')
|
|
95
|
+
expect(circles[1]?.getAttribute('r')).toBe('3')
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('DualPanel — fixed bottomYDomain ships verbatim (un-niced)', () => {
|
|
100
|
+
test('bottom axis ticks come from the pinned domain, not the computed max-abs', () => {
|
|
101
|
+
// Data's max abs is 0.02 — an auto domain would never produce a "5.00" tick. Pinning
|
|
102
|
+
// bottomYDomain to [-5, 5] must still paint it.
|
|
103
|
+
expect(renderStatic(undefined, [-5, 5])).toContain('>5.00<')
|
|
104
|
+
expect(renderStatic()).not.toContain('>5.00<')
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('DualPanel — formatBar overrides only the tooltip row', () => {
|
|
109
|
+
test('the bar tooltip row reads formatBar, not formatBottom', async () => {
|
|
110
|
+
render(
|
|
111
|
+
<DualPanel<Row>
|
|
112
|
+
data={rows}
|
|
113
|
+
chartId="dp-formatbar"
|
|
114
|
+
getX={(d) => d.date}
|
|
115
|
+
series={series}
|
|
116
|
+
getBar={(d) => d.bar}
|
|
117
|
+
barLabel="Bar"
|
|
118
|
+
barColorPositive="#0a0"
|
|
119
|
+
barColorNegative="#a00"
|
|
120
|
+
formatTop={(v) => String(v)}
|
|
121
|
+
formatBottom={(v) => v.toFixed(2)}
|
|
122
|
+
formatBar={(v, d) => `${v.toFixed(3)} m/s (${d.date})`}
|
|
123
|
+
/>,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
const slider = screen.getByRole('slider')
|
|
127
|
+
fireEvent.keyDown(slider, { key: 'ArrowRight' })
|
|
128
|
+
|
|
129
|
+
expect(await screen.findByText('0.020 m/s (2026-08-01)')).toBeTruthy()
|
|
130
|
+
})
|
|
131
|
+
})
|