@flesler/cursor-canvas-web 1.5.0 → 1.5.2
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/dist/cursor-canvas.cjs +1 -1
- package/dist/cursor-canvas.js +419 -404
- package/package.json +1 -1
- package/src/shim/__tests__/chartLegendOrder.test.tsx +34 -0
- package/src/shim/__tests__/useCanvasState.test.tsx +32 -0
- package/src/shim/bar-chart.tsx +2 -1
- package/src/shim/canvas-hooks.tsx +16 -4
- package/src/shim/chart-shared.tsx +8 -1
- package/src/shim/line-chart.tsx +5 -2
package/package.json
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { renderWithProvider } from '../../test/render'
|
|
3
|
+
import { BarChart } from '../bar-chart'
|
|
4
|
+
import { CHART_LEGEND_PROPS, CHART_TOOLTIP_PROPS } from '../chart-shared'
|
|
5
|
+
import { LineChart } from '../line-chart'
|
|
6
|
+
|
|
7
|
+
describe('chart label order', () => {
|
|
8
|
+
it('disables Recharts default legend and tooltip sorters', () => {
|
|
9
|
+
expect(CHART_LEGEND_PROPS.itemSorter).toBeNull()
|
|
10
|
+
expect(CHART_TOOLTIP_PROPS.itemSorter).toBeNull()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const series = [
|
|
14
|
+
{ name: 'Off-heap reclaimed', data: [12, 18], tone: 'info' as const },
|
|
15
|
+
{ name: 'Heap reclaimed', data: [58, 55], tone: 'warning' as const },
|
|
16
|
+
]
|
|
17
|
+
const categories = ['T+5s', 'T+10s']
|
|
18
|
+
const seriesNames = series.map(s => s.name)
|
|
19
|
+
|
|
20
|
+
it.each([
|
|
21
|
+
['LineChart', LineChart],
|
|
22
|
+
['BarChart', BarChart],
|
|
23
|
+
] as const)('keeps %s legend order in source series order', (_, Chart) => {
|
|
24
|
+
const { container } = renderWithProvider(
|
|
25
|
+
<Chart categories={categories} series={series} height={160} />,
|
|
26
|
+
)
|
|
27
|
+
const legend = container.querySelector('[class*="ChartLegend-legend"]')
|
|
28
|
+
expect(legend).toBeTruthy()
|
|
29
|
+
const names = legend!.querySelectorAll(
|
|
30
|
+
'[class*="ChartLegend-legendItemName"]',
|
|
31
|
+
)
|
|
32
|
+
expect([...names].map(el => el.textContent?.trim())).toEqual(seriesNames)
|
|
33
|
+
})
|
|
34
|
+
})
|
|
@@ -32,4 +32,36 @@ describe('useCanvasState', () => {
|
|
|
32
32
|
|
|
33
33
|
expect(result.current[0]).toBe(true)
|
|
34
34
|
})
|
|
35
|
+
|
|
36
|
+
it('falls back to __canvasJsonState when localStorage throws', () => {
|
|
37
|
+
const storage = Object.getOwnPropertyDescriptor(globalThis, 'localStorage')
|
|
38
|
+
Object.defineProperty(globalThis, 'localStorage', {
|
|
39
|
+
configurable: true,
|
|
40
|
+
get() {
|
|
41
|
+
throw new DOMException('sandboxed', 'SecurityError')
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
;(globalThis as { __canvasJsonState?: Record<string, unknown> }).__canvasJsonState = {
|
|
45
|
+
label: 'from-sidecar',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { result } = renderHook(() => useCanvasState('label', 'default'))
|
|
49
|
+
|
|
50
|
+
expect(result.current[0]).toBe('from-sidecar')
|
|
51
|
+
delete (globalThis as { __canvasJsonState?: Record<string, unknown> }).__canvasJsonState
|
|
52
|
+
if (storage) {
|
|
53
|
+
Object.defineProperty(globalThis, 'localStorage', storage)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('reads global __canvasJsonState when localStorage has no value', () => {
|
|
58
|
+
;(globalThis as { __canvasJsonState?: Record<string, unknown> }).__canvasJsonState = {
|
|
59
|
+
label: 'from-sidecar',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { result } = renderHook(() => useCanvasState('label', 'default'))
|
|
63
|
+
|
|
64
|
+
expect(result.current[0]).toBe('from-sidecar')
|
|
65
|
+
delete (globalThis as { __canvasJsonState?: Record<string, unknown> }).__canvasJsonState
|
|
66
|
+
})
|
|
35
67
|
})
|
package/src/shim/bar-chart.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BarChart as MantineBarChart } from '@mantine/charts'
|
|
2
2
|
import '@mantine/charts/styles.css'
|
|
3
3
|
import type { BarChartProps } from '../superset/index'
|
|
4
|
-
import { barValueLabelProps, CHART_LEGEND_PROPS, CHART_LEGEND_STYLES, defaultShowBarValues, horizontalBarProps, horizontalBarValueLabelProps, reshapeLineChartData, toMantineSeries, useChartColorProps, useSeriesColor, valueAxisChartProps, valueFormatter, verticalBarLabelProps, withPerCategoryBarColors } from './chart-shared'
|
|
4
|
+
import { barValueLabelProps, CHART_LEGEND_PROPS, CHART_LEGEND_STYLES, CHART_TOOLTIP_PROPS, defaultShowBarValues, horizontalBarProps, horizontalBarValueLabelProps, reshapeLineChartData, toMantineSeries, useChartColorProps, useSeriesColor, valueAxisChartProps, valueFormatter, verticalBarLabelProps, withPerCategoryBarColors } from './chart-shared'
|
|
5
5
|
|
|
6
6
|
export function BarChart({
|
|
7
7
|
categories,
|
|
@@ -52,6 +52,7 @@ export function BarChart({
|
|
|
52
52
|
orientation={horizontal ? 'vertical' : 'horizontal'}
|
|
53
53
|
withLegend={series.length > 1}
|
|
54
54
|
legendProps={CHART_LEGEND_PROPS}
|
|
55
|
+
tooltipProps={CHART_TOOLTIP_PROPS}
|
|
55
56
|
valueFormatter={valueFormatter(valueSuffix, valuePrefix)}
|
|
56
57
|
withBarValueLabel={withBarValueLabel}
|
|
57
58
|
{...(withBarValueLabel
|
|
@@ -8,13 +8,15 @@ export function useCanvasState<T>(
|
|
|
8
8
|
const storageKey = `canvas:${key}`
|
|
9
9
|
|
|
10
10
|
const [state, setState] = useState<T>(() => {
|
|
11
|
-
if (typeof localStorage === 'undefined') return defaultValue
|
|
12
11
|
try {
|
|
13
12
|
const raw = localStorage.getItem(storageKey)
|
|
14
|
-
|
|
13
|
+
if (raw !== null) return JSON.parse(raw) as T
|
|
15
14
|
} catch {
|
|
16
|
-
|
|
15
|
+
// Sandboxed, SSR, or unavailable; fall through to inlined export state.
|
|
17
16
|
}
|
|
17
|
+
const inline = inlineCanvasState<T>(key)
|
|
18
|
+
if (inline !== undefined) return inline
|
|
19
|
+
return defaultValue
|
|
18
20
|
})
|
|
19
21
|
|
|
20
22
|
const set = useCallback<SetCanvasState<T>>(
|
|
@@ -25,7 +27,7 @@ export function useCanvasState<T>(
|
|
|
25
27
|
? (action as (prev: T) => T)(prev)
|
|
26
28
|
: action
|
|
27
29
|
try {
|
|
28
|
-
localStorage
|
|
30
|
+
localStorage.setItem(storageKey, JSON.stringify(next))
|
|
29
31
|
} catch {
|
|
30
32
|
// Ignore quota / unavailable storage; state still updates in memory.
|
|
31
33
|
}
|
|
@@ -55,3 +57,13 @@ export function mergeStyle(
|
|
|
55
57
|
): CSSProperties {
|
|
56
58
|
return { ...base, ...override }
|
|
57
59
|
}
|
|
60
|
+
|
|
61
|
+
function inlineCanvasState<T>(key: string): T | undefined {
|
|
62
|
+
try {
|
|
63
|
+
const bag = (globalThis as { __canvasJsonState?: Record<string, unknown> }).__canvasJsonState
|
|
64
|
+
if (!bag || !Object.hasOwn(bag, key)) return undefined
|
|
65
|
+
return bag[key] as T
|
|
66
|
+
} catch {
|
|
67
|
+
return undefined
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared chart data shaping and axis helpers (no @mantine/charts component imports).
|
|
3
3
|
*/
|
|
4
4
|
import type { ChartReferenceLineProps } from '@mantine/charts'
|
|
5
|
-
import type { XAxisProps, YAxisProps } from 'recharts'
|
|
5
|
+
import type { LegendProps, XAxisProps, YAxisProps } from 'recharts'
|
|
6
6
|
import type { CanvasChartValueAxisExtra } from '../superset/chart-extra'
|
|
7
7
|
import type { BarChartProps } from '../superset/index'
|
|
8
8
|
import { toneHex } from './theme'
|
|
@@ -26,9 +26,16 @@ export type {
|
|
|
26
26
|
|
|
27
27
|
import type { ChartReferenceLine, ChartSeries, ChartTone } from '../vendor/canvas-sdk/chart-primitives.js'
|
|
28
28
|
|
|
29
|
+
/** Recharts 3 defaults to sorting legend rows; keep SDK `series[]` order. */
|
|
29
30
|
export const CHART_LEGEND_PROPS = {
|
|
30
31
|
verticalAlign: 'bottom' as const,
|
|
31
32
|
align: 'center' as const,
|
|
33
|
+
itemSorter: null,
|
|
34
|
+
} satisfies Pick<LegendProps, 'verticalAlign' | 'align' | 'itemSorter'>
|
|
35
|
+
|
|
36
|
+
/** Same for tooltip series rows (default `itemSorter: 'name'`). `null` disables sort at runtime. */
|
|
37
|
+
export const CHART_TOOLTIP_PROPS = {
|
|
38
|
+
itemSorter: null as never,
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
export const CHART_LEGEND_STYLES = {
|
package/src/shim/line-chart.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import type { LineChartCurveType } from '@mantine/charts'
|
|
|
2
2
|
import { AreaChart, LineChart as MantineLineChart } from '@mantine/charts'
|
|
3
3
|
import '@mantine/charts/styles.css'
|
|
4
4
|
import type { LineChartProps } from '../superset/index'
|
|
5
|
-
import { CHART_LEGEND_PROPS, CHART_LEGEND_STYLES, reshapeLineChartData, toMantineSeries, useChartColorProps, useSeriesColor, valueAxisChartProps, valueFormatter } from './chart-shared'
|
|
5
|
+
import { CHART_LEGEND_PROPS, CHART_LEGEND_STYLES, CHART_TOOLTIP_PROPS, reshapeLineChartData, toMantineSeries, useChartColorProps, useSeriesColor, valueAxisChartProps, valueFormatter } from './chart-shared'
|
|
6
6
|
|
|
7
7
|
export function LineChart({
|
|
8
8
|
categories,
|
|
@@ -46,7 +46,10 @@ export function LineChart({
|
|
|
46
46
|
legendProps: CHART_LEGEND_PROPS,
|
|
47
47
|
valueFormatter: valueFormatter(valueSuffix, valuePrefix),
|
|
48
48
|
withPointLabels: showValues ?? categories.length <= 20,
|
|
49
|
-
tooltipProps:
|
|
49
|
+
tooltipProps: {
|
|
50
|
+
...CHART_TOOLTIP_PROPS,
|
|
51
|
+
...(!showHoverGuide && { cursor: false }),
|
|
52
|
+
},
|
|
50
53
|
styles: {
|
|
51
54
|
...CHART_LEGEND_STYLES,
|
|
52
55
|
legendItemName: { color: chartColors.textColor },
|