waveform-analysis 0.1.63 → 0.1.65

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.
Files changed (97) hide show
  1. package/README.md +21 -5
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +986 -964
  4. package/dist/style.css +1 -1
  5. package/dist/types/components/core/constants.d.ts +1 -1
  6. package/dist/types/components/core/trackLayoutBuilder.d.ts +1 -0
  7. package/dist/types/components/core/yAxisConstants.d.ts +1 -1
  8. package/dist/types/types/chart.d.ts +6 -1
  9. package/package.json +1 -1
  10. package/src/App.vue +2 -2
  11. package/src/components/WaveformChart.css +4 -0
  12. package/src/components/WaveformChart.vue +2 -2
  13. package/src/components/core/constants.ts +3 -3
  14. package/src/components/core/layout.ts +1 -1
  15. package/src/components/core/trackLayoutBuilder.ts +42 -1
  16. package/src/components/core/useWaveformLayout.ts +2 -1
  17. package/src/components/core/useWaveformPresentation.ts +1 -1
  18. package/src/components/core/xDomain.ts +26 -2
  19. package/src/components/core/yAxisConstants.ts +1 -1
  20. package/src/components/rendering/WaveformLegend.vue +1 -1
  21. package/src/components/rendering/WaveformSeriesLayer.vue +1 -1
  22. package/src/components/rendering/WaveformTrack.css +5 -2
  23. package/src/components/rendering/WaveformTrack.vue +7 -1
  24. package/src/components/rendering/WaveformTrackAxes.vue +8 -2
  25. package/src/demo/DemoChartHost.vue +1 -0
  26. package/src/types/chart.ts +6 -1
  27. package/src/App.test.ts +0 -400
  28. package/src/components/annotation/components.test.ts +0 -372
  29. package/src/components/annotation/markup.test.ts +0 -371
  30. package/src/components/annotation/serialization.test.ts +0 -114
  31. package/src/components/core/constants.test.ts +0 -29
  32. package/src/components/core/fixedYDomainLayout.test.ts +0 -204
  33. package/src/components/core/grid.test.ts +0 -197
  34. package/src/components/core/latestTaskScheduler.test.ts +0 -51
  35. package/src/components/core/layout.test.ts +0 -373
  36. package/src/components/core/title.test.ts +0 -76
  37. package/src/components/core/useWaveformData.test.ts +0 -95
  38. package/src/components/core/useWaveformRenderSampling.test.ts +0 -17
  39. package/src/components/core/xDomain.test.ts +0 -28
  40. package/src/components/core/yAxisNice.test.ts +0 -174
  41. package/src/components/core/yAxisSlotLayout.test.ts +0 -165
  42. package/src/components/core/yDomain.test.ts +0 -56
  43. package/src/components/interaction/WaveformTooltip.test.ts +0 -276
  44. package/src/components/interaction/viewportInteractionState.test.ts +0 -101
  45. package/src/components/utils/useAnimationFrameThrottle.test.ts +0 -53
  46. package/src/components/waveformChartCases/annotationEditingAndReprojection.test.ts +0 -257
  47. package/src/components/waveformChartCases/compactAndSharedHover.test.ts +0 -358
  48. package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +0 -358
  49. package/src/components/waveformChartCases/dataLifecycle.test.ts +0 -310
  50. package/src/components/waveformChartCases/frameGridAndFormatting.test.ts +0 -389
  51. package/src/components/waveformChartCases/gridFill.test.ts +0 -95
  52. package/src/components/waveformChartCases/incompleteDataHover.test.ts +0 -155
  53. package/src/components/waveformChartCases/interactionAndAnnotationCreation.test.ts +0 -328
  54. package/src/components/waveformChartCases/layoutAndDomains.test.ts +0 -371
  55. package/src/components/waveformChartCases/legendVisibility.test.ts +0 -350
  56. package/src/components/waveformChartCases/multiAxisAndPagination.test.ts +0 -400
  57. package/src/components/waveformChartCases/multiAxisLabelAlignment.test.ts +0 -61
  58. package/src/components/waveformChartCases/presentationAndAxes.test.ts +0 -345
  59. package/src/components/waveformChartCases/presentationMode.test.ts +0 -327
  60. package/src/components/waveformChartCases/renderingStyles.test.ts +0 -283
  61. package/src/components/waveformChartCases/sizingAndTitles.test.ts +0 -370
  62. package/src/components/waveformChartCases/trackOrder.test.ts +0 -323
  63. package/src/components/waveformChartCases/viewportBoxAndPan.test.ts +0 -365
  64. package/src/components/waveformChartCases/viewportConstraintsAndReset.test.ts +0 -311
  65. package/src/components/waveformChartCases/viewportLifecycle.test.ts +0 -132
  66. package/src/components/waveformChartCases/viewportReset.test.ts +0 -251
  67. package/src/components/waveformChartCases/wheelZoomOut.test.ts +0 -114
  68. package/src/components/waveformChartCases/workerSamplingIntegration.test.ts +0 -117
  69. package/src/components/waveformChartCases/xDomainStrategy.test.ts +0 -168
  70. package/src/components/waveformChartCases/yAxisLayout.test.ts +0 -103
  71. package/src/components/waveformChartCases/zoomIntentAndDataRebind.test.ts +0 -213
  72. package/src/components/waveformChartCases/zoomPointConstraints.test.ts +0 -308
  73. package/src/components/waveformLegendGeometry.test.ts +0 -21
  74. package/src/components/waveformNormalization.test.ts +0 -170
  75. package/src/core/data.test.ts +0 -241
  76. package/src/core/rendering.test.ts +0 -267
  77. package/src/core/renderingStrategies.test.ts +0 -90
  78. package/src/core/samplingReference.test.ts +0 -124
  79. package/src/core/wasmSampling.test.ts +0 -205
  80. package/src/core/workerSampling/multiresolution.test.ts +0 -69
  81. package/src/core/workerSampling/protocol.test.ts +0 -34
  82. package/src/core/workerSampling/repository.test.ts +0 -400
  83. package/src/core/workerSampling/repositoryCache.test.ts +0 -163
  84. package/src/core/workerSampling/repositorySampling.test.ts +0 -106
  85. package/src/core/workerSampling/repositoryTypedDataset.test.ts +0 -89
  86. package/src/core/workerSampling/repositoryWasmDataset.test.ts +0 -70
  87. package/src/data/simulatedWaveforms.test.ts +0 -60
  88. package/src/demo/plotMarginControls.test.ts +0 -29
  89. package/src/demo/xAxisLabelControls.test.ts +0 -86
  90. package/src/test/setup.ts +0 -82
  91. package/src/test/waveformChart.ts +0 -75
  92. package/src/utils/domain.test.ts +0 -28
  93. package/src/utils/formatters.test.ts +0 -131
  94. package/src/utils/sampling.test.ts +0 -226
  95. package/src/views/FixedYDomainDemo.test.ts +0 -44
  96. package/src/views/WasmSamplingDemo.test.ts +0 -13
  97. package/src/views/wasmSamplingDemoSampling.test.ts +0 -27
@@ -1,345 +0,0 @@
1
- import { flushPromises } from '@vue/test-utils'
2
- import { describe, expect, it } from 'vitest'
3
-
4
- import { type WaveformData } from '../waveform'
5
-
6
- import { gridSeries, mountSizedChart } from '../../test/waveformChart'
7
-
8
- describe('WaveformChart', () => {
9
- it('keeps clip path ids unique across chart instances', async () => {
10
- const data: WaveformData = {
11
- kind: 'series',
12
- series: [
13
- {
14
- id: 'channel-1',
15
- name: '通道 1',
16
- data: {
17
- kind: 'points',
18
- points: [
19
- { x: 0, y: 0 },
20
- { x: 1, y: 1 },
21
- ],
22
- },
23
- },
24
- ],
25
- }
26
- const first = await mountSizedChart(data)
27
- const second = await mountSizedChart(data)
28
- const firstClipPathId = first.get('clipPath').attributes('id')
29
- const secondClipPathId = second.get('clipPath').attributes('id')
30
-
31
- expect(firstClipPathId).toBeTruthy()
32
- expect(secondClipPathId).toBeTruthy()
33
- expect(firstClipPathId).not.toBe(secondClipPathId)
34
- expect(first.get('[clip-path]').attributes('clip-path')).toContain(firstClipPathId)
35
- expect(second.get('[clip-path]').attributes('clip-path')).toContain(secondClipPathId)
36
-
37
- first.unmount()
38
- second.unmount()
39
- })
40
-
41
- it('does not reuse Y scales between chart instances with the same default series ID', async () => {
42
- const first = await mountSizedChart({
43
- kind: 'points',
44
- points: [
45
- { x: 0, y: 0 },
46
- { x: 1, y: 1 },
47
- ],
48
- })
49
- const second = await mountSizedChart({
50
- kind: 'points',
51
- points: [
52
- { x: 0, y: 10_000 },
53
- { x: 1, y: 20_000 },
54
- ],
55
- })
56
-
57
- expect(first.get('.waveform-chart__axis--y').text()).not.toContain('E')
58
- expect(second.get('.waveform-chart__axis--y').text()).toContain('E+04 ')
59
- expect(second.find('.waveform-chart__axis-exponent--y').exists()).toBe(false)
60
- })
61
-
62
- it('renders a configurable zero line only when the Y domain contains zero', async () => {
63
- const wrapper = await mountSizedChart(
64
- {
65
- kind: 'points',
66
- points: [
67
- { x: 0, y: -2 },
68
- { x: 1, y: 4 },
69
- ],
70
- },
71
- { zeroLine: { visible: true, color: '#475467', width: 2, dash: '3 2' } },
72
- )
73
-
74
- const zeroLine = wrapper.get('.waveform-chart__zero-line')
75
- expect(zeroLine.attributes()).toMatchObject({
76
- stroke: '#475467',
77
- 'stroke-width': '2',
78
- 'stroke-dasharray': '3 2',
79
- 'data-y-axis-index': '0',
80
- })
81
- expect(zeroLine.attributes('y1')).toBe(zeroLine.attributes('y2'))
82
-
83
- await wrapper.setProps({ zeroLine: { visible: false } })
84
- expect(wrapper.find('.waveform-chart__zero-line').exists()).toBe(false)
85
-
86
- const positive = await mountSizedChart(
87
- {
88
- kind: 'points',
89
- points: [
90
- { x: 0, y: 2 },
91
- { x: 1, y: 4 },
92
- ],
93
- },
94
- { zeroLine: { visible: true } },
95
- )
96
- expect(positive.find('.waveform-chart__zero-line').exists()).toBe(false)
97
- })
98
-
99
- it('renders zero lines from each visible Y axis in multi-axis mode', async () => {
100
- const wrapper = await mountSizedChart(
101
- {
102
- kind: 'series',
103
- series: [
104
- {
105
- id: 'small',
106
- trackId: 'overlay',
107
- name: 'small',
108
- data: {
109
- kind: 'points',
110
- points: [
111
- { x: 0, y: -1 },
112
- { x: 1, y: 3 },
113
- ],
114
- },
115
- },
116
- {
117
- id: 'large',
118
- trackId: 'overlay',
119
- name: 'large',
120
- data: {
121
- kind: 'points',
122
- points: [
123
- { x: 0, y: -10 },
124
- { x: 1, y: 2 },
125
- ],
126
- },
127
- },
128
- ],
129
- },
130
- { overlayMode: 'multi-axis', zeroLine: { visible: true } },
131
- )
132
-
133
- const zeroLines = wrapper.findAll('.waveform-chart__zero-line')
134
- expect(zeroLines).toHaveLength(2)
135
- expect(zeroLines.map((line) => line.attributes('data-y-axis-index'))).toEqual(['0', '1'])
136
- expect(zeroLines[0].attributes('y1')).not.toBe(zeroLines[1].attributes('y1'))
137
- })
138
-
139
- it('hides X and Y axis lines independently while preserving axis text and the frame', async () => {
140
- const wrapper = await mountSizedChart({
141
- kind: 'series',
142
- series: [
143
- {
144
- id: 'channel',
145
- name: 'Voltage',
146
- data: {
147
- kind: 'points',
148
- points: [
149
- { x: 0, y: -1 },
150
- { x: 10_000, y: 1 },
151
- ],
152
- },
153
- },
154
- ],
155
- })
156
- const xAxis = wrapper.get('.waveform-chart__axis--x')
157
- const yAxis = wrapper.get('.waveform-chart__axis--y')
158
-
159
- expect(xAxis.classes()).not.toContain('waveform-track__axis--line-hidden')
160
- expect(yAxis.classes()).not.toContain('waveform-track__axis--line-hidden')
161
-
162
- await wrapper.setProps({ axes: { x: { lineVisible: false } } })
163
- await flushPromises()
164
- expect(xAxis.classes()).toContain('waveform-track__axis--line-hidden')
165
- expect(yAxis.classes()).not.toContain('waveform-track__axis--line-hidden')
166
- expect(xAxis.get('path.domain').attributes('display')).toBe('none')
167
- expect(
168
- xAxis.findAll('.tick line').every((line) => line.attributes('display') === undefined),
169
- ).toBe(true)
170
- expect(yAxis.get('path.domain').attributes('display')).toBeUndefined()
171
-
172
- await wrapper.setProps({
173
- axes: {
174
- x: { lineVisible: false },
175
- y: { lineVisible: false },
176
- },
177
- grid: {
178
- rowCount: 1,
179
- columnCount: 1,
180
- trackLines: { channel: { horizontal: false, vertical: false } },
181
- },
182
- frameStyle: { borderColor: '#dc2626', borderWidth: 2 },
183
- })
184
- await flushPromises()
185
-
186
- expect(xAxis.classes()).toContain('waveform-track__axis--line-hidden')
187
- expect(yAxis.classes()).toContain('waveform-track__axis--line-hidden')
188
- expect(yAxis.get('path.domain').attributes('display')).toBe('none')
189
- expect(
190
- yAxis.findAll('.tick line').every((line) => line.attributes('display') === undefined),
191
- ).toBe(true)
192
- expect(xAxis.findAll('text').length).toBeGreaterThan(0)
193
- expect(yAxis.findAll('text').length).toBeGreaterThan(0)
194
- expect(wrapper.findAll('.waveform-chart__axis-endpoint')).toHaveLength(2)
195
- expect(wrapper.get('.waveform-chart__y-axis-label').text()).toBe('Voltage')
196
- expect(wrapper.findAll('[data-grid-direction]')).toHaveLength(0)
197
- expect(wrapper.get('.waveform-chart__plot-frame').attributes()).toMatchObject({
198
- stroke: '#dc2626',
199
- 'stroke-width': '2',
200
- })
201
-
202
- await wrapper.setProps({ axes: { x: { lineVisible: true }, y: { lineVisible: true } } })
203
- await flushPromises()
204
- expect(xAxis.get('path.domain').attributes('display')).toBeUndefined()
205
- expect(yAxis.get('path.domain').attributes('display')).toBeUndefined()
206
- })
207
-
208
- it('preserves a titled multi-axis plot and hides auxiliary layers in clean view', async () => {
209
- const data: WaveformData = {
210
- kind: 'series',
211
- series: [
212
- {
213
- id: 'first',
214
- trackId: 'overlay',
215
- name: 'first',
216
- data: {
217
- kind: 'points',
218
- points: [
219
- { x: 0, y: -1 },
220
- { x: 1, y: 1 },
221
- ],
222
- },
223
- },
224
- {
225
- id: 'second',
226
- trackId: 'overlay',
227
- name: 'second',
228
- data: {
229
- kind: 'points',
230
- points: [
231
- { x: 0, y: 10_000 },
232
- { x: 10_000, y: 20_000 },
233
- ],
234
- },
235
- },
236
- {
237
- id: 'third',
238
- name: 'third',
239
- data: {
240
- kind: 'points',
241
- points: [
242
- { x: 0, y: 2 },
243
- { x: 10_000, y: 3 },
244
- ],
245
- },
246
- },
247
- ],
248
- }
249
- const sharedProps = {
250
- grid: { rowCount: 1, columnCount: 1, showPagination: true },
251
- overlayMode: 'multi-axis' as const,
252
- frameNumber: 1,
253
- annotations: [{ id: 'note', seriesId: 'first', x: 0.5, y: 0, text: 'hidden note' }],
254
- zeroLine: { visible: true },
255
- title: { text: 'hidden title' },
256
- axes: { x: { lineVisible: false }, y: { lineVisible: false } },
257
- frameStyle: { borderColor: '#dc2626', borderWidth: 2, borderStyle: 'dashed' as const },
258
- }
259
- const regularWrapper = await mountSizedChart(data, sharedProps)
260
- const wrapper = await mountSizedChart(data, {
261
- ...sharedProps,
262
- cleanView: true,
263
- })
264
-
265
- const regularTrack = regularWrapper.get('.waveform-chart__track')
266
- const cleanTrack = wrapper.get('.waveform-chart__track')
267
- const geometryAttributes = [
268
- 'data-track-left',
269
- 'data-track-top',
270
- 'data-track-width',
271
- 'data-track-height',
272
- ]
273
-
274
- expect(wrapper.get('.waveform-chart').attributes('data-chart-left-margin')).toBe(
275
- regularWrapper.get('.waveform-chart').attributes('data-chart-left-margin'),
276
- )
277
- expect(wrapper.get('.waveform-chart').attributes('data-title-area-height')).toBe(
278
- regularWrapper.get('.waveform-chart').attributes('data-title-area-height'),
279
- )
280
- geometryAttributes.forEach((attribute) => {
281
- expect(cleanTrack.attributes(attribute)).toBe(regularTrack.attributes(attribute))
282
- })
283
- expect(wrapper.get('.waveform-chart').classes()).toContain('waveform-chart--clean')
284
- expect(getComputedStyle(wrapper.get('.waveform-chart').element).borderColor).toBe(
285
- 'rgba(0, 0, 0, 0)',
286
- )
287
- expect(wrapper.findAll('.waveform-chart__series')).toHaveLength(2)
288
- expect(wrapper.find('.waveform-chart__overlay--independent').exists()).toBe(true)
289
- expect(wrapper.get('.waveform-chart__title-area').attributes('aria-hidden')).toBe('true')
290
- expect(wrapper.find('.waveform-chart__title-visual').exists()).toBe(false)
291
- const xAxis = wrapper.get('.waveform-chart__axis--x')
292
- const yAxes = wrapper.findAll('.waveform-chart__axis--y')
293
- expect(yAxes).toHaveLength(2)
294
- expect(xAxis.findAll('.tick line').length).toBeGreaterThan(0)
295
- expect(xAxis.findAll('.tick text').length).toBeGreaterThan(0)
296
- yAxes.forEach((axis) => {
297
- expect(axis.findAll('.tick line').length).toBeGreaterThan(0)
298
- expect(axis.findAll('.tick text').length).toBeGreaterThan(0)
299
- expect(axis.get('path.domain').attributes('display')).toBe('none')
300
- })
301
- expect(xAxis.get('path.domain').attributes('display')).toBe('none')
302
- expect(wrapper.findAll('.waveform-chart__axis-endpoint')).toHaveLength(2)
303
- expect(wrapper.find('.waveform-chart__axis-exponent--x').exists()).toBe(false)
304
- expect(yAxes.some((axis) => axis.text().includes('E+04 '))).toBe(true)
305
- expect(wrapper.find('.waveform-chart__axis-exponent--y').exists()).toBe(false)
306
- expect(wrapper.find('.waveform-chart__grid').exists()).toBe(false)
307
- expect(wrapper.get('.waveform-chart__plot-frame').attributes()).toMatchObject({
308
- stroke: '#dc2626',
309
- 'stroke-width': '2',
310
- 'stroke-dasharray': '6 4',
311
- })
312
- expect(wrapper.find('.waveform-chart__plot-background').exists()).toBe(false)
313
- expect(wrapper.find('.waveform-chart__watermark').exists()).toBe(false)
314
- expect(wrapper.find('.waveform-chart__legend-layer').exists()).toBe(false)
315
- expect(wrapper.find('.waveform-chart__label').exists()).toBe(false)
316
- expect(wrapper.find('.waveform-chart__y-axis-label').exists()).toBe(false)
317
- expect(wrapper.find('.waveform-chart__zero-line').exists()).toBe(false)
318
- expect(wrapper.find('.waveform-annotation-layer').exists()).toBe(false)
319
- expect(wrapper.find('.ant-pagination').exists()).toBe(false)
320
- })
321
-
322
- it('preserves every track geometry in a multi-column clean view', async () => {
323
- const props = {
324
- displayMode: 'independent' as const,
325
- grid: { rowCount: 2, columnCount: 2 },
326
- }
327
- const regularWrapper = await mountSizedChart(gridSeries(4), props)
328
- const cleanWrapper = await mountSizedChart(gridSeries(4), { ...props, cleanView: true })
329
- const geometryAttributes = [
330
- 'data-track-left',
331
- 'data-track-top',
332
- 'data-track-width',
333
- 'data-track-height',
334
- ]
335
- const regularTracks = regularWrapper.findAll('.waveform-chart__track')
336
- const cleanTracks = cleanWrapper.findAll('.waveform-chart__track')
337
-
338
- expect(cleanTracks).toHaveLength(regularTracks.length)
339
- cleanTracks.forEach((track, index) => {
340
- geometryAttributes.forEach((attribute) => {
341
- expect(track.attributes(attribute)).toBe(regularTracks[index].attributes(attribute))
342
- })
343
- })
344
- })
345
- })
@@ -1,327 +0,0 @@
1
- import { flushPromises } from '@vue/test-utils'
2
- import { describe, expect, it } from 'vitest'
3
-
4
- import { flushAnimationFrames } from '../../test/setup'
5
- import { mountSizedChart } from '../../test/waveformChart'
6
- import type { WaveformData, WaveformDisplayMode } from '../data/types'
7
-
8
- function annotationEditorExists() {
9
- return Boolean(
10
- Array.from(document.body.querySelectorAll<HTMLElement>('[role="dialog"]'))
11
- .filter((element) => element.closest('.waveform-annotation-editor'))
12
- .at(-1),
13
- )
14
- }
15
-
16
- const presentationData: WaveformData = {
17
- kind: 'series',
18
- series: [
19
- {
20
- id: 'low',
21
- trackId: 'shared',
22
- name: '低量程',
23
- data: {
24
- kind: 'points',
25
- points: [
26
- { x: 0, y: 0 },
27
- { x: 1, y: 10 },
28
- { x: 2, y: 5 },
29
- ],
30
- },
31
- },
32
- {
33
- id: 'high',
34
- trackId: 'shared',
35
- name: '高量程',
36
- data: {
37
- kind: 'points',
38
- points: [
39
- { x: 0, y: 100 },
40
- { x: 1, y: 200 },
41
- { x: 2, y: 150 },
42
- ],
43
- },
44
- },
45
- {
46
- id: 'mid',
47
- trackId: 'shared',
48
- name: '中量程',
49
- data: {
50
- kind: 'points',
51
- points: [
52
- { x: 0, y: 50 },
53
- { x: 1, y: 100 },
54
- { x: 2, y: 75 },
55
- ],
56
- },
57
- },
58
- {
59
- id: 'other',
60
- name: '其他通道',
61
- data: {
62
- kind: 'points',
63
- points: [
64
- { x: 0, y: -1 },
65
- { x: 1, y: 1 },
66
- { x: 2, y: 0 },
67
- ],
68
- },
69
- },
70
- ],
71
- }
72
-
73
- function setOverlayBounds(overlay: ReturnType<Awaited<ReturnType<typeof mountSizedChart>>['get']>) {
74
- const width = Number(overlay.attributes('width'))
75
- const height = Number(overlay.attributes('height'))
76
- Object.defineProperty(overlay.element, 'getBoundingClientRect', {
77
- value: () => ({ left: 0, top: 0, width, height }),
78
- })
79
- return { width, height }
80
- }
81
-
82
- function dispatchPointer(
83
- element: Element,
84
- type: string,
85
- options: MouseEventInit & { pointerId: number },
86
- ) {
87
- const event = new MouseEvent(type, { bubbles: true, cancelable: true, ...options })
88
- Object.defineProperty(event, 'pointerId', { value: options.pointerId })
89
- element.dispatchEvent(event)
90
- }
91
-
92
- describe('WaveformChart presentation mode', () => {
93
- it('keeps existing plot interaction enabled by default', async () => {
94
- const wrapper = await mountSizedChart(presentationData, {
95
- grid: { rowCount: 1, columnCount: 1 },
96
- })
97
- const overlay = wrapper.get('.waveform-chart__overlay--independent')
98
- const { width, height } = setOverlayBounds(overlay)
99
-
100
- expect(wrapper.attributes('data-presentation-mode')).toBe('false')
101
- expect(overlay.classes()).toContain('is-zoomable')
102
-
103
- overlay.element.dispatchEvent(
104
- new MouseEvent('pointermove', {
105
- clientX: width / 2,
106
- clientY: height / 2,
107
- bubbles: true,
108
- }),
109
- )
110
- flushAnimationFrames()
111
- await flushPromises()
112
-
113
- expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(true)
114
- expect(wrapper.emitted('point-hover')?.length).toBeGreaterThan(0)
115
- })
116
-
117
- it.each<WaveformDisplayMode>(['independent', 'separated'])(
118
- 'blocks plot mouse events in %s layout',
119
- async (displayMode) => {
120
- const wrapper = await mountSizedChart(presentationData, {
121
- annotations: [{ id: 'note', seriesId: 'low', x: 1, y: 10, text: '只读标注' }],
122
- displayMode,
123
- grid: { rowCount: 1, columnCount: 1 },
124
- interactionMode: 'annotation',
125
- presentationMode: true,
126
- })
127
- const overlay = wrapper.get('.waveform-chart__overlay')
128
- const { width, height } = setOverlayBounds(overlay)
129
-
130
- expect(wrapper.attributes('data-presentation-mode')).toBe('true')
131
- expect(overlay.classes()).not.toContain('is-zoomable')
132
- expect(overlay.classes()).not.toContain('is-annotating')
133
-
134
- overlay.element.dispatchEvent(
135
- new MouseEvent('pointermove', {
136
- clientX: width / 2,
137
- clientY: height / 2,
138
- bubbles: true,
139
- }),
140
- )
141
- overlay.element.dispatchEvent(
142
- new WheelEvent('wheel', {
143
- deltaY: -4000,
144
- clientX: width / 2,
145
- clientY: height / 2,
146
- bubbles: true,
147
- cancelable: true,
148
- }),
149
- )
150
- overlay.element.dispatchEvent(
151
- new MouseEvent('click', {
152
- clientX: width / 2,
153
- clientY: height / 2,
154
- bubbles: true,
155
- cancelable: true,
156
- }),
157
- )
158
- overlay.element.dispatchEvent(
159
- new MouseEvent('contextmenu', {
160
- clientX: width / 2,
161
- clientY: height / 2,
162
- bubbles: true,
163
- cancelable: true,
164
- }),
165
- )
166
- wrapper
167
- .get('.waveform-chart__svg')
168
- .element.dispatchEvent(new MouseEvent('dblclick', { bubbles: true, cancelable: true }))
169
- await wrapper.get('[data-annotation-id="note"]').trigger('contextmenu', {
170
- clientX: width / 2,
171
- clientY: height / 2,
172
- })
173
- const annotation = wrapper.get('[data-annotation-id="note"]').element
174
- dispatchPointer(annotation, 'pointerdown', {
175
- button: 0,
176
- clientX: width / 2,
177
- clientY: height / 2,
178
- pointerId: 42,
179
- })
180
- dispatchPointer(annotation, 'pointermove', {
181
- clientX: width / 2 + 30,
182
- clientY: height / 2 + 20,
183
- pointerId: 42,
184
- })
185
- dispatchPointer(annotation, 'pointerup', {
186
- clientX: width / 2 + 30,
187
- clientY: height / 2 + 20,
188
- pointerId: 42,
189
- })
190
- flushAnimationFrames()
191
- await flushPromises()
192
-
193
- expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(false)
194
- expect(wrapper.find('.waveform-annotation-editor').exists()).toBe(false)
195
- expect(wrapper.find('.waveform-annotation-context-menu').exists()).toBe(false)
196
- expect(wrapper.emitted('point-hover')).toBeUndefined()
197
- expect(wrapper.emitted('zoom-change')).toBeUndefined()
198
- expect(wrapper.emitted('zoom-end')).toBeUndefined()
199
- expect(wrapper.emitted('zoom-reset')).toBeUndefined()
200
- expect(wrapper.emitted('update:annotations')).toBeUndefined()
201
- },
202
- )
203
-
204
- it('keeps the legend and pagination interactive', async () => {
205
- const wrapper = await mountSizedChart(presentationData, {
206
- grid: { rowCount: 1, columnCount: 1, showPagination: true },
207
- legend: { interactive: true },
208
- presentationMode: true,
209
- })
210
-
211
- await wrapper.findAll('.waveform-chart__legend-item')[1]!.trigger('click')
212
- await flushPromises()
213
- expect(wrapper.emitted('update:hidden-series-ids')?.at(-1)).toEqual([['high']])
214
-
215
- await wrapper.get('.ant-pagination-next button').trigger('click')
216
- await flushPromises()
217
- expect(wrapper.emitted('page-change')?.at(-1)).toEqual([2, 2])
218
- })
219
-
220
- it('cleans active UI state and restores interaction without resetting the viewport', async () => {
221
- const wrapper = await mountSizedChart(presentationData, {
222
- grid: { rowCount: 1, columnCount: 1 },
223
- })
224
- const overlay = wrapper.get('.waveform-chart__overlay--independent')
225
- const { width, height } = setOverlayBounds(overlay)
226
-
227
- overlay.element.dispatchEvent(
228
- new WheelEvent('wheel', {
229
- deltaY: -4000,
230
- clientX: width / 2,
231
- clientY: height / 2,
232
- bubbles: true,
233
- cancelable: true,
234
- }),
235
- )
236
- flushAnimationFrames()
237
- await flushPromises()
238
- const zoomedEndpoint = wrapper.get('.waveform-chart__axis-endpoint--end').text()
239
-
240
- dispatchPointer(overlay.element, 'pointerdown', {
241
- button: 0,
242
- clientX: width * 0.25,
243
- clientY: height / 2,
244
- pointerId: 41,
245
- })
246
- dispatchPointer(overlay.element, 'pointermove', {
247
- clientX: width * 0.75,
248
- clientY: height / 2,
249
- pointerId: 41,
250
- })
251
- await flushPromises()
252
- expect(wrapper.find('.waveform-chart__zoom-selection').exists()).toBe(true)
253
-
254
- await wrapper.setProps({ presentationMode: true })
255
- await flushPromises()
256
-
257
- expect(wrapper.find('.waveform-chart__zoom-selection').exists()).toBe(false)
258
- expect(wrapper.get('.waveform-chart__axis-endpoint--end').text()).toBe(zoomedEndpoint)
259
-
260
- const zoomEventCount = wrapper.emitted('zoom-change')?.length ?? 0
261
- await wrapper.setProps({ presentationMode: false })
262
- await flushPromises()
263
- overlay.element.dispatchEvent(
264
- new WheelEvent('wheel', {
265
- deltaY: 200,
266
- clientX: width / 2,
267
- clientY: height / 2,
268
- bubbles: true,
269
- cancelable: true,
270
- }),
271
- )
272
- flushAnimationFrames()
273
- await flushPromises()
274
-
275
- expect(wrapper.emitted('zoom-change')?.length).toBeGreaterThan(zoomEventCount)
276
- })
277
-
278
- it('closes hover and annotation UI when enabled at runtime', async () => {
279
- const wrapper = await mountSizedChart(presentationData, {
280
- grid: { rowCount: 1, columnCount: 1 },
281
- interactionMode: 'annotation',
282
- })
283
- const overlay = wrapper.get('.waveform-chart__overlay--independent')
284
- const { width, height } = setOverlayBounds(overlay)
285
-
286
- overlay.element.dispatchEvent(
287
- new MouseEvent('pointermove', {
288
- clientX: width / 2,
289
- clientY: height / 2,
290
- bubbles: true,
291
- }),
292
- )
293
- flushAnimationFrames()
294
- await flushPromises()
295
- expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(true)
296
-
297
- overlay.element.dispatchEvent(
298
- new MouseEvent('contextmenu', {
299
- clientX: width / 2,
300
- clientY: height / 2,
301
- bubbles: true,
302
- cancelable: true,
303
- }),
304
- )
305
- await flushPromises()
306
- expect(annotationEditorExists()).toBe(true)
307
-
308
- await wrapper.setProps({ presentationMode: true })
309
- await flushPromises()
310
-
311
- expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(false)
312
- expect(annotationEditorExists()).toBe(false)
313
- expect(wrapper.emitted('point-hover')?.at(-1)).toEqual([null])
314
-
315
- await wrapper.setProps({ presentationMode: false })
316
- overlay.element.dispatchEvent(
317
- new MouseEvent('pointermove', {
318
- clientX: width / 2,
319
- clientY: height / 2,
320
- bubbles: true,
321
- }),
322
- )
323
- flushAnimationFrames()
324
- await flushPromises()
325
- expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(true)
326
- })
327
- })