@pyreon/charts 0.11.5 → 0.11.6
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/README.md +31 -31
- package/lib/index.js.map +1 -1
- package/lib/manual.js.map +1 -1
- package/lib/types/index.d.ts +2 -2
- package/lib/types/manual.d.ts +2 -2
- package/package.json +18 -18
- package/src/chart-component.tsx +9 -9
- package/src/index.ts +3 -3
- package/src/loader.ts +54 -54
- package/src/manual.ts +4 -9
- package/src/tests/charts.test.tsx +178 -178
- package/src/types.ts +7 -7
- package/src/use-chart.ts +7 -7
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { signal } from
|
|
2
|
-
import { mount } from
|
|
3
|
-
import { Chart } from
|
|
4
|
-
import { _resetLoader, ensureModules, getCore, getCoreSync, manualUse } from
|
|
1
|
+
import { signal } from '@pyreon/reactivity'
|
|
2
|
+
import { mount } from '@pyreon/runtime-dom'
|
|
3
|
+
import { Chart } from '../chart-component'
|
|
4
|
+
import { _resetLoader, ensureModules, getCore, getCoreSync, manualUse } from '../loader'
|
|
5
5
|
|
|
6
6
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
7
7
|
|
|
8
8
|
function mountWith<T>(fn: () => T): { result: T; unmount: () => void } {
|
|
9
9
|
let result: T | undefined
|
|
10
|
-
const el = document.createElement(
|
|
10
|
+
const el = document.createElement('div')
|
|
11
11
|
document.body.appendChild(el)
|
|
12
12
|
const Child = () => {
|
|
13
13
|
result = fn()
|
|
@@ -29,120 +29,120 @@ afterEach(() => {
|
|
|
29
29
|
|
|
30
30
|
// ─── Loader ───────────────────────────────────────────────────────────────────
|
|
31
31
|
|
|
32
|
-
describe(
|
|
33
|
-
it(
|
|
32
|
+
describe('loader', () => {
|
|
33
|
+
it('lazily loads echarts/core on first call', async () => {
|
|
34
34
|
const core = await getCore()
|
|
35
35
|
expect(core).toBeDefined()
|
|
36
|
-
expect(typeof core.init).toBe(
|
|
37
|
-
expect(typeof core.use).toBe(
|
|
36
|
+
expect(typeof core.init).toBe('function')
|
|
37
|
+
expect(typeof core.use).toBe('function')
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
it(
|
|
40
|
+
it('caches core module on subsequent calls', async () => {
|
|
41
41
|
const core1 = await getCore()
|
|
42
42
|
const core2 = await getCore()
|
|
43
43
|
expect(core1).toBe(core2)
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
it(
|
|
46
|
+
it('auto-detects BarChart from series type', async () => {
|
|
47
47
|
const core = await ensureModules({
|
|
48
|
-
series: [{ type:
|
|
48
|
+
series: [{ type: 'bar', data: [1, 2, 3] }],
|
|
49
49
|
})
|
|
50
50
|
expect(core).toBeDefined()
|
|
51
51
|
// If it didn't throw, the modules were registered successfully
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
it(
|
|
54
|
+
it('auto-detects PieChart from series type', async () => {
|
|
55
55
|
const core = await ensureModules({
|
|
56
|
-
series: [{ type:
|
|
56
|
+
series: [{ type: 'pie', data: [{ value: 1 }] }],
|
|
57
57
|
})
|
|
58
58
|
expect(core).toBeDefined()
|
|
59
59
|
})
|
|
60
60
|
|
|
61
|
-
it(
|
|
61
|
+
it('auto-detects LineChart from series type', async () => {
|
|
62
62
|
const core = await ensureModules({
|
|
63
|
-
series: [{ type:
|
|
63
|
+
series: [{ type: 'line', data: [1, 2, 3] }],
|
|
64
64
|
})
|
|
65
65
|
expect(core).toBeDefined()
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
-
it(
|
|
68
|
+
it('auto-detects components from config keys', async () => {
|
|
69
69
|
const core = await ensureModules({
|
|
70
|
-
tooltip: { trigger:
|
|
70
|
+
tooltip: { trigger: 'axis' },
|
|
71
71
|
legend: {},
|
|
72
|
-
xAxis: { type:
|
|
73
|
-
yAxis: { type:
|
|
74
|
-
series: [{ type:
|
|
72
|
+
xAxis: { type: 'category' },
|
|
73
|
+
yAxis: { type: 'value' },
|
|
74
|
+
series: [{ type: 'bar', data: [1] }],
|
|
75
75
|
})
|
|
76
76
|
expect(core).toBeDefined()
|
|
77
77
|
})
|
|
78
78
|
|
|
79
|
-
it(
|
|
79
|
+
it('auto-detects series features (markPoint, markLine, markArea)', async () => {
|
|
80
80
|
const core = await ensureModules({
|
|
81
81
|
series: [
|
|
82
82
|
{
|
|
83
|
-
type:
|
|
83
|
+
type: 'line',
|
|
84
84
|
data: [1, 2, 3],
|
|
85
|
-
markPoint: { data: [{ type:
|
|
86
|
-
markLine: { data: [{ type:
|
|
87
|
-
markArea: { data: [[{ xAxis:
|
|
85
|
+
markPoint: { data: [{ type: 'max' }] },
|
|
86
|
+
markLine: { data: [{ type: 'average' }] },
|
|
87
|
+
markArea: { data: [[{ xAxis: 'A' }, { xAxis: 'B' }]] },
|
|
88
88
|
},
|
|
89
89
|
],
|
|
90
90
|
})
|
|
91
91
|
expect(core).toBeDefined()
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
-
it(
|
|
94
|
+
it('loads multiple chart types in one config', async () => {
|
|
95
95
|
const core = await ensureModules({
|
|
96
96
|
series: [
|
|
97
|
-
{ type:
|
|
98
|
-
{ type:
|
|
99
|
-
{ type:
|
|
97
|
+
{ type: 'bar', data: [1, 2] },
|
|
98
|
+
{ type: 'line', data: [3, 4] },
|
|
99
|
+
{ type: 'scatter', data: [[1, 2]] },
|
|
100
100
|
],
|
|
101
101
|
})
|
|
102
102
|
expect(core).toBeDefined()
|
|
103
103
|
})
|
|
104
104
|
|
|
105
|
-
it(
|
|
105
|
+
it('caches modules across calls', async () => {
|
|
106
106
|
// First call loads BarChart
|
|
107
|
-
await ensureModules({ series: [{ type:
|
|
107
|
+
await ensureModules({ series: [{ type: 'bar', data: [1] }] })
|
|
108
108
|
// Second call should be instant (cached)
|
|
109
109
|
const start = performance.now()
|
|
110
|
-
await ensureModules({ series: [{ type:
|
|
110
|
+
await ensureModules({ series: [{ type: 'bar', data: [2] }] })
|
|
111
111
|
const duration = performance.now() - start
|
|
112
112
|
// Should be near-instant since modules are cached
|
|
113
113
|
expect(duration).toBeLessThan(50)
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
it(
|
|
116
|
+
it('handles series as single object (not array)', async () => {
|
|
117
117
|
const core = await ensureModules({
|
|
118
|
-
series: { type:
|
|
118
|
+
series: { type: 'bar', data: [1, 2, 3] },
|
|
119
119
|
})
|
|
120
120
|
expect(core).toBeDefined()
|
|
121
121
|
})
|
|
122
122
|
|
|
123
|
-
it(
|
|
123
|
+
it('handles empty series gracefully', async () => {
|
|
124
124
|
const core = await ensureModules({ series: [] })
|
|
125
125
|
expect(core).toBeDefined()
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
-
it(
|
|
129
|
-
const core = await ensureModules({ title: { text:
|
|
128
|
+
it('handles config with no series', async () => {
|
|
129
|
+
const core = await ensureModules({ title: { text: 'Hello' } })
|
|
130
130
|
expect(core).toBeDefined()
|
|
131
131
|
})
|
|
132
132
|
|
|
133
|
-
it(
|
|
133
|
+
it('ignores unknown chart types', async () => {
|
|
134
134
|
const core = await ensureModules({
|
|
135
|
-
series: [{ type:
|
|
135
|
+
series: [{ type: 'nonexistent', data: [1] }],
|
|
136
136
|
})
|
|
137
137
|
expect(core).toBeDefined()
|
|
138
138
|
})
|
|
139
139
|
|
|
140
|
-
it(
|
|
141
|
-
const core = await ensureModules({ series: [{ type:
|
|
140
|
+
it('loads SVG renderer when specified', async () => {
|
|
141
|
+
const core = await ensureModules({ series: [{ type: 'bar', data: [1] }] }, 'svg')
|
|
142
142
|
expect(core).toBeDefined()
|
|
143
143
|
})
|
|
144
144
|
|
|
145
|
-
it(
|
|
145
|
+
it('resets state with _resetLoader', async () => {
|
|
146
146
|
await getCore() // load core
|
|
147
147
|
_resetLoader()
|
|
148
148
|
// After reset, core should be null internally but getCore still works
|
|
@@ -150,35 +150,35 @@ describe("loader", () => {
|
|
|
150
150
|
expect(core).toBeDefined()
|
|
151
151
|
})
|
|
152
152
|
|
|
153
|
-
it(
|
|
153
|
+
it('getCoreSync returns null before loading', () => {
|
|
154
154
|
_resetLoader()
|
|
155
155
|
expect(getCoreSync()).toBeNull()
|
|
156
156
|
})
|
|
157
157
|
|
|
158
|
-
it(
|
|
158
|
+
it('getCoreSync returns core after loading', async () => {
|
|
159
159
|
await getCore()
|
|
160
160
|
expect(getCoreSync()).not.toBeNull()
|
|
161
|
-
expect(typeof getCoreSync()!.init).toBe(
|
|
161
|
+
expect(typeof getCoreSync()!.init).toBe('function')
|
|
162
162
|
})
|
|
163
163
|
|
|
164
|
-
it(
|
|
164
|
+
it('manualUse registers modules when core is loaded', async () => {
|
|
165
165
|
await getCore() // ensure core is loaded
|
|
166
166
|
// Should not throw — registers module with core.use()
|
|
167
|
-
const { CanvasRenderer } = await import(
|
|
167
|
+
const { CanvasRenderer } = await import('echarts/renderers')
|
|
168
168
|
expect(() => manualUse(CanvasRenderer)).not.toThrow()
|
|
169
169
|
})
|
|
170
170
|
|
|
171
|
-
it(
|
|
171
|
+
it('manualUse queues modules when core is not yet loaded', async () => {
|
|
172
172
|
_resetLoader()
|
|
173
173
|
// Core not loaded yet — should queue, not throw
|
|
174
|
-
const { CanvasRenderer } = await import(
|
|
174
|
+
const { CanvasRenderer } = await import('echarts/renderers')
|
|
175
175
|
expect(() => manualUse(CanvasRenderer)).not.toThrow()
|
|
176
176
|
})
|
|
177
177
|
|
|
178
|
-
it(
|
|
178
|
+
it('loads radar component for radar config key', async () => {
|
|
179
179
|
const core = await ensureModules({
|
|
180
|
-
radar: { indicator: [{ name:
|
|
181
|
-
series: [{ type:
|
|
180
|
+
radar: { indicator: [{ name: 'A' }] },
|
|
181
|
+
series: [{ type: 'radar', data: [{ value: [1] }] }],
|
|
182
182
|
})
|
|
183
183
|
expect(core).toBeDefined()
|
|
184
184
|
})
|
|
@@ -186,36 +186,36 @@ describe("loader", () => {
|
|
|
186
186
|
|
|
187
187
|
// ─── Chart component ─────────────────────────────────────────────────────────
|
|
188
188
|
|
|
189
|
-
describe(
|
|
190
|
-
it(
|
|
191
|
-
const container = document.createElement(
|
|
189
|
+
describe('Chart component', () => {
|
|
190
|
+
it('renders a div element', () => {
|
|
191
|
+
const container = document.createElement('div')
|
|
192
192
|
document.body.appendChild(container)
|
|
193
193
|
|
|
194
194
|
const unmount = mount(
|
|
195
195
|
<Chart
|
|
196
196
|
options={() => ({
|
|
197
|
-
series: [{ type:
|
|
197
|
+
series: [{ type: 'bar', data: [1, 2, 3] }],
|
|
198
198
|
})}
|
|
199
199
|
style="height: 300px"
|
|
200
200
|
/>,
|
|
201
201
|
container,
|
|
202
202
|
)
|
|
203
203
|
|
|
204
|
-
const div = container.querySelector(
|
|
204
|
+
const div = container.querySelector('div')
|
|
205
205
|
expect(div).not.toBeNull()
|
|
206
206
|
|
|
207
207
|
unmount()
|
|
208
208
|
container.remove()
|
|
209
209
|
})
|
|
210
210
|
|
|
211
|
-
it(
|
|
212
|
-
const container = document.createElement(
|
|
211
|
+
it('applies style and class props to container', () => {
|
|
212
|
+
const container = document.createElement('div')
|
|
213
213
|
document.body.appendChild(container)
|
|
214
214
|
|
|
215
215
|
const unmount = mount(
|
|
216
216
|
<Chart
|
|
217
217
|
options={() => ({
|
|
218
|
-
series: [{ type:
|
|
218
|
+
series: [{ type: 'bar', data: [1] }],
|
|
219
219
|
})}
|
|
220
220
|
style="height: 400px; width: 100%"
|
|
221
221
|
class="revenue-chart"
|
|
@@ -223,9 +223,9 @@ describe("Chart component", () => {
|
|
|
223
223
|
container,
|
|
224
224
|
)
|
|
225
225
|
|
|
226
|
-
const div = container.querySelector(
|
|
227
|
-
expect(div?.getAttribute(
|
|
228
|
-
expect(div?.getAttribute(
|
|
226
|
+
const div = container.querySelector('div')
|
|
227
|
+
expect(div?.getAttribute('style')).toContain('height: 400px')
|
|
228
|
+
expect(div?.getAttribute('class')).toContain('revenue-chart')
|
|
229
229
|
|
|
230
230
|
unmount()
|
|
231
231
|
container.remove()
|
|
@@ -234,31 +234,31 @@ describe("Chart component", () => {
|
|
|
234
234
|
|
|
235
235
|
// ─── useChart basic API ──────────────────────────────────────────────────────
|
|
236
236
|
|
|
237
|
-
describe(
|
|
237
|
+
describe('useChart API', () => {
|
|
238
238
|
// Import useChart lazily to avoid module-level echarts import issues
|
|
239
|
-
it(
|
|
240
|
-
const { useChart } = await import(
|
|
239
|
+
it('returns the correct shape', async () => {
|
|
240
|
+
const { useChart } = await import('../use-chart')
|
|
241
241
|
|
|
242
242
|
const { result: chart, unmount } = mountWith(() =>
|
|
243
243
|
useChart(() => ({
|
|
244
|
-
series: [{ type:
|
|
244
|
+
series: [{ type: 'bar', data: [1, 2, 3] }],
|
|
245
245
|
})),
|
|
246
246
|
)
|
|
247
247
|
|
|
248
|
-
expect(typeof chart.ref).toBe(
|
|
248
|
+
expect(typeof chart.ref).toBe('function')
|
|
249
249
|
expect(chart.instance()).toBeNull()
|
|
250
250
|
expect(chart.loading()).toBe(true)
|
|
251
|
-
expect(typeof chart.resize).toBe(
|
|
251
|
+
expect(typeof chart.resize).toBe('function')
|
|
252
252
|
|
|
253
253
|
unmount()
|
|
254
254
|
})
|
|
255
255
|
|
|
256
|
-
it(
|
|
257
|
-
const { useChart } = await import(
|
|
256
|
+
it('resize does not throw when no instance', async () => {
|
|
257
|
+
const { useChart } = await import('../use-chart')
|
|
258
258
|
|
|
259
259
|
const { result: chart, unmount } = mountWith(() =>
|
|
260
260
|
useChart(() => ({
|
|
261
|
-
series: [{ type:
|
|
261
|
+
series: [{ type: 'bar', data: [1] }],
|
|
262
262
|
})),
|
|
263
263
|
)
|
|
264
264
|
|
|
@@ -266,12 +266,12 @@ describe("useChart API", () => {
|
|
|
266
266
|
unmount()
|
|
267
267
|
})
|
|
268
268
|
|
|
269
|
-
it(
|
|
270
|
-
const { useChart } = await import(
|
|
269
|
+
it('loading starts as true', async () => {
|
|
270
|
+
const { useChart } = await import('../use-chart')
|
|
271
271
|
|
|
272
272
|
const { result: chart, unmount } = mountWith(() =>
|
|
273
273
|
useChart(() => ({
|
|
274
|
-
series: [{ type:
|
|
274
|
+
series: [{ type: 'pie', data: [{ value: 1 }] }],
|
|
275
275
|
})),
|
|
276
276
|
)
|
|
277
277
|
|
|
@@ -282,30 +282,30 @@ describe("useChart API", () => {
|
|
|
282
282
|
|
|
283
283
|
// ─── All supported chart types ──────────────────────────────────────────────
|
|
284
284
|
|
|
285
|
-
describe(
|
|
285
|
+
describe('supported chart types', () => {
|
|
286
286
|
const chartTypes = [
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
287
|
+
'bar',
|
|
288
|
+
'line',
|
|
289
|
+
'pie',
|
|
290
|
+
'scatter',
|
|
291
|
+
'radar',
|
|
292
|
+
'heatmap',
|
|
293
|
+
'treemap',
|
|
294
|
+
'sunburst',
|
|
295
|
+
'sankey',
|
|
296
|
+
'funnel',
|
|
297
|
+
'gauge',
|
|
298
|
+
'graph',
|
|
299
|
+
'tree',
|
|
300
|
+
'boxplot',
|
|
301
|
+
'candlestick',
|
|
302
|
+
'parallel',
|
|
303
|
+
'themeRiver',
|
|
304
|
+
'effectScatter',
|
|
305
|
+
'lines',
|
|
306
|
+
'pictorialBar',
|
|
307
|
+
'custom',
|
|
308
|
+
'map',
|
|
309
309
|
]
|
|
310
310
|
|
|
311
311
|
for (const type of chartTypes) {
|
|
@@ -321,25 +321,25 @@ describe("supported chart types", () => {
|
|
|
321
321
|
|
|
322
322
|
// ─── All supported component keys ───────────────────────────────────────────
|
|
323
323
|
|
|
324
|
-
describe(
|
|
324
|
+
describe('supported component keys', () => {
|
|
325
325
|
const componentKeys = [
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
326
|
+
'tooltip',
|
|
327
|
+
'legend',
|
|
328
|
+
'title',
|
|
329
|
+
'toolbox',
|
|
330
|
+
'dataZoom',
|
|
331
|
+
'visualMap',
|
|
332
|
+
'timeline',
|
|
333
|
+
'graphic',
|
|
334
|
+
'brush',
|
|
335
|
+
'calendar',
|
|
336
|
+
'dataset',
|
|
337
|
+
'aria',
|
|
338
|
+
'grid',
|
|
339
|
+
'xAxis',
|
|
340
|
+
'yAxis',
|
|
341
|
+
'polar',
|
|
342
|
+
'geo',
|
|
343
343
|
]
|
|
344
344
|
|
|
345
345
|
for (const key of componentKeys) {
|
|
@@ -347,7 +347,7 @@ describe("supported component keys", () => {
|
|
|
347
347
|
_resetLoader()
|
|
348
348
|
const core = await ensureModules({
|
|
349
349
|
[key]: {},
|
|
350
|
-
series: [{ type:
|
|
350
|
+
series: [{ type: 'bar', data: [1] }],
|
|
351
351
|
})
|
|
352
352
|
expect(core).toBeDefined()
|
|
353
353
|
})
|
|
@@ -356,28 +356,28 @@ describe("supported component keys", () => {
|
|
|
356
356
|
|
|
357
357
|
// ─── Option validation ──────────────────────────────────────────────────────
|
|
358
358
|
|
|
359
|
-
describe(
|
|
360
|
-
it(
|
|
359
|
+
describe('option validation', () => {
|
|
360
|
+
it('handles empty options object', async () => {
|
|
361
361
|
const core = await ensureModules({})
|
|
362
362
|
expect(core).toBeDefined()
|
|
363
363
|
})
|
|
364
364
|
|
|
365
|
-
it(
|
|
365
|
+
it('throws on null series entries', async () => {
|
|
366
366
|
await expect(
|
|
367
367
|
ensureModules({
|
|
368
|
-
series: [null as any, { type:
|
|
368
|
+
series: [null as any, { type: 'bar', data: [1] }],
|
|
369
369
|
}),
|
|
370
370
|
).rejects.toThrow()
|
|
371
371
|
})
|
|
372
372
|
|
|
373
|
-
it(
|
|
373
|
+
it('handles series with no type property', async () => {
|
|
374
374
|
const core = await ensureModules({
|
|
375
375
|
series: [{ data: [1, 2, 3] }],
|
|
376
376
|
})
|
|
377
377
|
expect(core).toBeDefined()
|
|
378
378
|
})
|
|
379
379
|
|
|
380
|
-
it(
|
|
380
|
+
it('handles undefined series value', async () => {
|
|
381
381
|
const core = await ensureModules({ series: undefined })
|
|
382
382
|
expect(core).toBeDefined()
|
|
383
383
|
})
|
|
@@ -385,19 +385,19 @@ describe("option validation", () => {
|
|
|
385
385
|
|
|
386
386
|
// ─── Multiple chart instances ───────────────────────────────────────────────
|
|
387
387
|
|
|
388
|
-
describe(
|
|
389
|
-
it(
|
|
390
|
-
const { useChart } = await import(
|
|
388
|
+
describe('multiple chart instances', () => {
|
|
389
|
+
it('creates independent useChart instances', async () => {
|
|
390
|
+
const { useChart } = await import('../use-chart')
|
|
391
391
|
|
|
392
392
|
const { result: chart1, unmount: unmount1 } = mountWith(() =>
|
|
393
393
|
useChart(() => ({
|
|
394
|
-
series: [{ type:
|
|
394
|
+
series: [{ type: 'bar', data: [1, 2, 3] }],
|
|
395
395
|
})),
|
|
396
396
|
)
|
|
397
397
|
|
|
398
398
|
const { result: chart2, unmount: unmount2 } = mountWith(() =>
|
|
399
399
|
useChart(() => ({
|
|
400
|
-
series: [{ type:
|
|
400
|
+
series: [{ type: 'line', data: [4, 5, 6] }],
|
|
401
401
|
})),
|
|
402
402
|
)
|
|
403
403
|
|
|
@@ -410,18 +410,18 @@ describe("multiple chart instances", () => {
|
|
|
410
410
|
unmount2()
|
|
411
411
|
})
|
|
412
412
|
|
|
413
|
-
it(
|
|
414
|
-
const { useChart } = await import(
|
|
413
|
+
it('disposing one chart does not affect another', async () => {
|
|
414
|
+
const { useChart } = await import('../use-chart')
|
|
415
415
|
|
|
416
416
|
const { unmount: unmount1 } = mountWith(() =>
|
|
417
417
|
useChart(() => ({
|
|
418
|
-
series: [{ type:
|
|
418
|
+
series: [{ type: 'bar', data: [1] }],
|
|
419
419
|
})),
|
|
420
420
|
)
|
|
421
421
|
|
|
422
422
|
const { result: chart2, unmount: unmount2 } = mountWith(() =>
|
|
423
423
|
useChart(() => ({
|
|
424
|
-
series: [{ type:
|
|
424
|
+
series: [{ type: 'bar', data: [2] }],
|
|
425
425
|
})),
|
|
426
426
|
)
|
|
427
427
|
|
|
@@ -438,13 +438,13 @@ describe("multiple chart instances", () => {
|
|
|
438
438
|
|
|
439
439
|
// ─── Resize observer cleanup ────────────────────────────────────────────────
|
|
440
440
|
|
|
441
|
-
describe(
|
|
442
|
-
it(
|
|
443
|
-
const { useChart } = await import(
|
|
441
|
+
describe('resize observer cleanup', () => {
|
|
442
|
+
it('chart instance is set to null on unmount', async () => {
|
|
443
|
+
const { useChart } = await import('../use-chart')
|
|
444
444
|
|
|
445
445
|
const { result: chart, unmount } = mountWith(() =>
|
|
446
446
|
useChart(() => ({
|
|
447
|
-
series: [{ type:
|
|
447
|
+
series: [{ type: 'bar', data: [1] }],
|
|
448
448
|
})),
|
|
449
449
|
)
|
|
450
450
|
|
|
@@ -457,19 +457,19 @@ describe("resize observer cleanup", () => {
|
|
|
457
457
|
expect(chart.instance()).toBeNull()
|
|
458
458
|
})
|
|
459
459
|
|
|
460
|
-
it(
|
|
461
|
-
const { useChart } = await import(
|
|
460
|
+
it('onUnmount disposes the chart instance when it exists', async () => {
|
|
461
|
+
const { useChart } = await import('../use-chart')
|
|
462
462
|
|
|
463
|
-
const el = document.createElement(
|
|
463
|
+
const el = document.createElement('div')
|
|
464
464
|
document.body.appendChild(el)
|
|
465
465
|
|
|
466
|
-
const mountEl = document.createElement(
|
|
466
|
+
const mountEl = document.createElement('div')
|
|
467
467
|
document.body.appendChild(mountEl)
|
|
468
468
|
|
|
469
469
|
let chartResult: ReturnType<typeof useChart> | undefined
|
|
470
470
|
const Child = () => {
|
|
471
471
|
chartResult = useChart(() => ({
|
|
472
|
-
series: [{ type:
|
|
472
|
+
series: [{ type: 'bar', data: [1] }],
|
|
473
473
|
}))
|
|
474
474
|
chartResult.ref(el)
|
|
475
475
|
return null
|
|
@@ -493,16 +493,16 @@ describe("resize observer cleanup", () => {
|
|
|
493
493
|
|
|
494
494
|
// ─── Theme switching ────────────────────────────────────────────────────────
|
|
495
495
|
|
|
496
|
-
describe(
|
|
497
|
-
it(
|
|
498
|
-
const { useChart } = await import(
|
|
496
|
+
describe('theme config', () => {
|
|
497
|
+
it('passes theme to useChart config', async () => {
|
|
498
|
+
const { useChart } = await import('../use-chart')
|
|
499
499
|
|
|
500
500
|
const { result: chart, unmount } = mountWith(() =>
|
|
501
501
|
useChart(
|
|
502
502
|
() => ({
|
|
503
|
-
series: [{ type:
|
|
503
|
+
series: [{ type: 'bar', data: [1] }],
|
|
504
504
|
}),
|
|
505
|
-
{ theme:
|
|
505
|
+
{ theme: 'dark' },
|
|
506
506
|
),
|
|
507
507
|
)
|
|
508
508
|
|
|
@@ -513,14 +513,14 @@ describe("theme config", () => {
|
|
|
513
513
|
unmount()
|
|
514
514
|
})
|
|
515
515
|
|
|
516
|
-
it(
|
|
517
|
-
const container = document.createElement(
|
|
516
|
+
it('Chart component accepts theme prop', () => {
|
|
517
|
+
const container = document.createElement('div')
|
|
518
518
|
document.body.appendChild(container)
|
|
519
519
|
|
|
520
520
|
const unmount = mount(
|
|
521
521
|
<Chart
|
|
522
522
|
options={() => ({
|
|
523
|
-
series: [{ type:
|
|
523
|
+
series: [{ type: 'bar', data: [1] }],
|
|
524
524
|
})}
|
|
525
525
|
theme="dark"
|
|
526
526
|
style="height: 300px"
|
|
@@ -528,7 +528,7 @@ describe("theme config", () => {
|
|
|
528
528
|
container,
|
|
529
529
|
)
|
|
530
530
|
|
|
531
|
-
const div = container.querySelector(
|
|
531
|
+
const div = container.querySelector('div')
|
|
532
532
|
expect(div).not.toBeNull()
|
|
533
533
|
|
|
534
534
|
unmount()
|
|
@@ -538,13 +538,13 @@ describe("theme config", () => {
|
|
|
538
538
|
|
|
539
539
|
// ─── Error signal ───────────────────────────────────────────────────────────
|
|
540
540
|
|
|
541
|
-
describe(
|
|
542
|
-
it(
|
|
543
|
-
const { useChart } = await import(
|
|
541
|
+
describe('error signal', () => {
|
|
542
|
+
it('error is null initially', async () => {
|
|
543
|
+
const { useChart } = await import('../use-chart')
|
|
544
544
|
|
|
545
545
|
const { result: chart, unmount } = mountWith(() =>
|
|
546
546
|
useChart(() => ({
|
|
547
|
-
series: [{ type:
|
|
547
|
+
series: [{ type: 'bar', data: [1] }],
|
|
548
548
|
})),
|
|
549
549
|
)
|
|
550
550
|
|
|
@@ -552,62 +552,62 @@ describe("error signal", () => {
|
|
|
552
552
|
unmount()
|
|
553
553
|
})
|
|
554
554
|
|
|
555
|
-
it(
|
|
556
|
-
const { useChart } = await import(
|
|
555
|
+
it('error is set when optionsFn throws during init', async () => {
|
|
556
|
+
const { useChart } = await import('../use-chart')
|
|
557
557
|
|
|
558
558
|
const { result: chart, unmount } = mountWith(() =>
|
|
559
559
|
useChart(() => {
|
|
560
|
-
throw new Error(
|
|
560
|
+
throw new Error('Bad options')
|
|
561
561
|
}),
|
|
562
562
|
)
|
|
563
563
|
|
|
564
564
|
// Trigger init by setting a container
|
|
565
|
-
const el = document.createElement(
|
|
565
|
+
const el = document.createElement('div')
|
|
566
566
|
document.body.appendChild(el)
|
|
567
567
|
chart.ref(el)
|
|
568
568
|
|
|
569
569
|
await new Promise((r) => setTimeout(r, 100))
|
|
570
570
|
|
|
571
571
|
expect(chart.error()).not.toBeNull()
|
|
572
|
-
expect(chart.error()!.message).toBe(
|
|
572
|
+
expect(chart.error()!.message).toBe('Bad options')
|
|
573
573
|
expect(chart.loading()).toBe(false)
|
|
574
574
|
|
|
575
575
|
unmount()
|
|
576
576
|
el.remove()
|
|
577
577
|
})
|
|
578
578
|
|
|
579
|
-
it(
|
|
580
|
-
const { useChart } = await import(
|
|
579
|
+
it('error is set when optionsFn throws non-Error value during init', async () => {
|
|
580
|
+
const { useChart } = await import('../use-chart')
|
|
581
581
|
|
|
582
582
|
const { result: chart, unmount } = mountWith(() =>
|
|
583
583
|
useChart(() => {
|
|
584
|
-
throw
|
|
584
|
+
throw 'string failure'
|
|
585
585
|
}),
|
|
586
586
|
)
|
|
587
587
|
|
|
588
|
-
const el = document.createElement(
|
|
588
|
+
const el = document.createElement('div')
|
|
589
589
|
document.body.appendChild(el)
|
|
590
590
|
chart.ref(el)
|
|
591
591
|
|
|
592
592
|
await new Promise((r) => setTimeout(r, 100))
|
|
593
593
|
|
|
594
594
|
expect(chart.error()).not.toBeNull()
|
|
595
|
-
expect(chart.error()!.message).toBe(
|
|
595
|
+
expect(chart.error()!.message).toBe('string failure')
|
|
596
596
|
|
|
597
597
|
unmount()
|
|
598
598
|
el.remove()
|
|
599
599
|
})
|
|
600
600
|
|
|
601
|
-
it(
|
|
602
|
-
const { useChart } = await import(
|
|
601
|
+
it('useChart returns error signal in result shape', async () => {
|
|
602
|
+
const { useChart } = await import('../use-chart')
|
|
603
603
|
|
|
604
604
|
const { result: chart, unmount } = mountWith(() =>
|
|
605
605
|
useChart(() => ({
|
|
606
|
-
series: [{ type:
|
|
606
|
+
series: [{ type: 'bar', data: [1] }],
|
|
607
607
|
})),
|
|
608
608
|
)
|
|
609
609
|
|
|
610
|
-
expect(typeof chart.error).toBe(
|
|
610
|
+
expect(typeof chart.error).toBe('function')
|
|
611
611
|
expect(chart.error()).toBeNull()
|
|
612
612
|
|
|
613
613
|
unmount()
|
|
@@ -616,9 +616,9 @@ describe("error signal", () => {
|
|
|
616
616
|
|
|
617
617
|
// ─── Chart component events ─────────────────────────────────────────────────
|
|
618
618
|
|
|
619
|
-
describe(
|
|
620
|
-
it(
|
|
621
|
-
const container = document.createElement(
|
|
619
|
+
describe('Chart component events', () => {
|
|
620
|
+
it('renders with event handler props without throwing', () => {
|
|
621
|
+
const container = document.createElement('div')
|
|
622
622
|
document.body.appendChild(container)
|
|
623
623
|
|
|
624
624
|
const onClick = vi.fn()
|
|
@@ -627,7 +627,7 @@ describe("Chart component events", () => {
|
|
|
627
627
|
const unmount = mount(
|
|
628
628
|
<Chart
|
|
629
629
|
options={() => ({
|
|
630
|
-
series: [{ type:
|
|
630
|
+
series: [{ type: 'bar', data: [1] }],
|
|
631
631
|
})}
|
|
632
632
|
style="height: 300px"
|
|
633
633
|
onClick={onClick}
|
|
@@ -636,7 +636,7 @@ describe("Chart component events", () => {
|
|
|
636
636
|
container,
|
|
637
637
|
)
|
|
638
638
|
|
|
639
|
-
expect(container.querySelector(
|
|
639
|
+
expect(container.querySelector('div')).not.toBeNull()
|
|
640
640
|
|
|
641
641
|
unmount()
|
|
642
642
|
container.remove()
|
|
@@ -645,15 +645,15 @@ describe("Chart component events", () => {
|
|
|
645
645
|
|
|
646
646
|
// ─── Reactive options with signal ───────────────────────────────────────────
|
|
647
647
|
|
|
648
|
-
describe(
|
|
649
|
-
it(
|
|
650
|
-
const { useChart } = await import(
|
|
648
|
+
describe('reactive options', () => {
|
|
649
|
+
it('useChart accepts optionsFn that reads signals', async () => {
|
|
650
|
+
const { useChart } = await import('../use-chart')
|
|
651
651
|
|
|
652
652
|
const data = signal([1, 2, 3])
|
|
653
653
|
|
|
654
654
|
const { result: chart, unmount } = mountWith(() =>
|
|
655
655
|
useChart(() => ({
|
|
656
|
-
series: [{ type:
|
|
656
|
+
series: [{ type: 'bar', data: data() }],
|
|
657
657
|
})),
|
|
658
658
|
)
|
|
659
659
|
|