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.
- package/README.md +21 -5
- package/dist/index.cjs +1 -1
- package/dist/index.js +986 -964
- package/dist/style.css +1 -1
- package/dist/types/components/core/constants.d.ts +1 -1
- package/dist/types/components/core/trackLayoutBuilder.d.ts +1 -0
- package/dist/types/components/core/yAxisConstants.d.ts +1 -1
- package/dist/types/types/chart.d.ts +6 -1
- package/package.json +1 -1
- package/src/App.vue +2 -2
- package/src/components/WaveformChart.css +4 -0
- package/src/components/WaveformChart.vue +2 -2
- package/src/components/core/constants.ts +3 -3
- package/src/components/core/layout.ts +1 -1
- package/src/components/core/trackLayoutBuilder.ts +42 -1
- package/src/components/core/useWaveformLayout.ts +2 -1
- package/src/components/core/useWaveformPresentation.ts +1 -1
- package/src/components/core/xDomain.ts +26 -2
- package/src/components/core/yAxisConstants.ts +1 -1
- package/src/components/rendering/WaveformLegend.vue +1 -1
- package/src/components/rendering/WaveformSeriesLayer.vue +1 -1
- package/src/components/rendering/WaveformTrack.css +5 -2
- package/src/components/rendering/WaveformTrack.vue +7 -1
- package/src/components/rendering/WaveformTrackAxes.vue +8 -2
- package/src/demo/DemoChartHost.vue +1 -0
- package/src/types/chart.ts +6 -1
- package/src/App.test.ts +0 -400
- package/src/components/annotation/components.test.ts +0 -372
- package/src/components/annotation/markup.test.ts +0 -371
- package/src/components/annotation/serialization.test.ts +0 -114
- package/src/components/core/constants.test.ts +0 -29
- package/src/components/core/fixedYDomainLayout.test.ts +0 -204
- package/src/components/core/grid.test.ts +0 -197
- package/src/components/core/latestTaskScheduler.test.ts +0 -51
- package/src/components/core/layout.test.ts +0 -373
- package/src/components/core/title.test.ts +0 -76
- package/src/components/core/useWaveformData.test.ts +0 -95
- package/src/components/core/useWaveformRenderSampling.test.ts +0 -17
- package/src/components/core/xDomain.test.ts +0 -28
- package/src/components/core/yAxisNice.test.ts +0 -174
- package/src/components/core/yAxisSlotLayout.test.ts +0 -165
- package/src/components/core/yDomain.test.ts +0 -56
- package/src/components/interaction/WaveformTooltip.test.ts +0 -276
- package/src/components/interaction/viewportInteractionState.test.ts +0 -101
- package/src/components/utils/useAnimationFrameThrottle.test.ts +0 -53
- package/src/components/waveformChartCases/annotationEditingAndReprojection.test.ts +0 -257
- package/src/components/waveformChartCases/compactAndSharedHover.test.ts +0 -358
- package/src/components/waveformChartCases/dataAndHoverPerformance.test.ts +0 -358
- package/src/components/waveformChartCases/dataLifecycle.test.ts +0 -310
- package/src/components/waveformChartCases/frameGridAndFormatting.test.ts +0 -389
- package/src/components/waveformChartCases/gridFill.test.ts +0 -95
- package/src/components/waveformChartCases/incompleteDataHover.test.ts +0 -155
- package/src/components/waveformChartCases/interactionAndAnnotationCreation.test.ts +0 -328
- package/src/components/waveformChartCases/layoutAndDomains.test.ts +0 -371
- package/src/components/waveformChartCases/legendVisibility.test.ts +0 -350
- package/src/components/waveformChartCases/multiAxisAndPagination.test.ts +0 -400
- package/src/components/waveformChartCases/multiAxisLabelAlignment.test.ts +0 -61
- package/src/components/waveformChartCases/presentationAndAxes.test.ts +0 -345
- package/src/components/waveformChartCases/presentationMode.test.ts +0 -327
- package/src/components/waveformChartCases/renderingStyles.test.ts +0 -283
- package/src/components/waveformChartCases/sizingAndTitles.test.ts +0 -370
- package/src/components/waveformChartCases/trackOrder.test.ts +0 -323
- package/src/components/waveformChartCases/viewportBoxAndPan.test.ts +0 -365
- package/src/components/waveformChartCases/viewportConstraintsAndReset.test.ts +0 -311
- package/src/components/waveformChartCases/viewportLifecycle.test.ts +0 -132
- package/src/components/waveformChartCases/viewportReset.test.ts +0 -251
- package/src/components/waveformChartCases/wheelZoomOut.test.ts +0 -114
- package/src/components/waveformChartCases/workerSamplingIntegration.test.ts +0 -117
- package/src/components/waveformChartCases/xDomainStrategy.test.ts +0 -168
- package/src/components/waveformChartCases/yAxisLayout.test.ts +0 -103
- package/src/components/waveformChartCases/zoomIntentAndDataRebind.test.ts +0 -213
- package/src/components/waveformChartCases/zoomPointConstraints.test.ts +0 -308
- package/src/components/waveformLegendGeometry.test.ts +0 -21
- package/src/components/waveformNormalization.test.ts +0 -170
- package/src/core/data.test.ts +0 -241
- package/src/core/rendering.test.ts +0 -267
- package/src/core/renderingStrategies.test.ts +0 -90
- package/src/core/samplingReference.test.ts +0 -124
- package/src/core/wasmSampling.test.ts +0 -205
- package/src/core/workerSampling/multiresolution.test.ts +0 -69
- package/src/core/workerSampling/protocol.test.ts +0 -34
- package/src/core/workerSampling/repository.test.ts +0 -400
- package/src/core/workerSampling/repositoryCache.test.ts +0 -163
- package/src/core/workerSampling/repositorySampling.test.ts +0 -106
- package/src/core/workerSampling/repositoryTypedDataset.test.ts +0 -89
- package/src/core/workerSampling/repositoryWasmDataset.test.ts +0 -70
- package/src/data/simulatedWaveforms.test.ts +0 -60
- package/src/demo/plotMarginControls.test.ts +0 -29
- package/src/demo/xAxisLabelControls.test.ts +0 -86
- package/src/test/setup.ts +0 -82
- package/src/test/waveformChart.ts +0 -75
- package/src/utils/domain.test.ts +0 -28
- package/src/utils/formatters.test.ts +0 -131
- package/src/utils/sampling.test.ts +0 -226
- package/src/views/FixedYDomainDemo.test.ts +0 -44
- package/src/views/WasmSamplingDemo.test.ts +0 -13
- package/src/views/wasmSamplingDemoSampling.test.ts +0 -27
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
reduceViewportInteraction,
|
|
5
|
-
transitionViewportInteraction,
|
|
6
|
-
type ViewportInteractionEvent,
|
|
7
|
-
} from './viewportInteractionState'
|
|
8
|
-
|
|
9
|
-
const gesture = {
|
|
10
|
-
trackIndex: 2,
|
|
11
|
-
independent: true,
|
|
12
|
-
startX: 10,
|
|
13
|
-
startY: 20,
|
|
14
|
-
pointerId: 7,
|
|
15
|
-
kind: 'box' as const,
|
|
16
|
-
xDomain: [0, 100] as [number, number],
|
|
17
|
-
yDomains: { track: [-1, 1] as [number, number] },
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe('viewport interaction reducer', () => {
|
|
21
|
-
it('accepts one begin and rejects a conflicting begin', () => {
|
|
22
|
-
const started = transitionViewportInteraction(null, { type: 'begin', gesture })
|
|
23
|
-
const conflicting = transitionViewportInteraction(started.state, {
|
|
24
|
-
type: 'begin',
|
|
25
|
-
gesture: { ...gesture, pointerId: 8 },
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
expect(started.accepted).toBe(true)
|
|
29
|
-
expect(started.state).toMatchObject({ kind: 'box', pointerId: 7 })
|
|
30
|
-
expect(conflicting.accepted).toBe(false)
|
|
31
|
-
expect(conflicting.state).toMatchObject({ kind: 'box', pointerId: 7 })
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('rejects move and finish from a different pointer without changing state', () => {
|
|
35
|
-
const state = reduceViewportInteraction(null, { type: 'begin', gesture })
|
|
36
|
-
const move = transitionViewportInteraction(state, {
|
|
37
|
-
type: 'move',
|
|
38
|
-
pointerId: 8,
|
|
39
|
-
position: { currentX: 30, currentY: 40 },
|
|
40
|
-
})
|
|
41
|
-
const finish = transitionViewportInteraction(move.state, {
|
|
42
|
-
type: 'finish',
|
|
43
|
-
pointerId: 8,
|
|
44
|
-
position: { currentX: 30, currentY: 40 },
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
expect(move.accepted).toBe(false)
|
|
48
|
-
expect(finish.accepted).toBe(false)
|
|
49
|
-
expect(finish.state).toMatchObject({ currentX: 10, currentY: 20 })
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('updates a valid pointer and returns the completed snapshot on finish', () => {
|
|
53
|
-
const state = reduceViewportInteraction(null, {
|
|
54
|
-
type: 'begin',
|
|
55
|
-
gesture: { ...gesture, kind: 'pan' },
|
|
56
|
-
})
|
|
57
|
-
const moved = transitionViewportInteraction(state, {
|
|
58
|
-
type: 'move',
|
|
59
|
-
pointerId: 7,
|
|
60
|
-
position: { currentX: 30, currentY: 40 },
|
|
61
|
-
})
|
|
62
|
-
const finished = transitionViewportInteraction(moved.state, {
|
|
63
|
-
type: 'finish',
|
|
64
|
-
pointerId: 7,
|
|
65
|
-
position: { currentX: 50, currentY: 60 },
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
expect(moved.state).toMatchObject({ kind: 'pan', currentX: 30, currentY: 40 })
|
|
69
|
-
expect(finished.completed).toMatchObject({ kind: 'pan', currentX: 50, currentY: 60 })
|
|
70
|
-
expect(finished.state).toBeNull()
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('cancels only the owning pointer and supports cancel/reset events', () => {
|
|
74
|
-
const state = reduceViewportInteraction(null, { type: 'begin', gesture })
|
|
75
|
-
const rejectedCancel = transitionViewportInteraction(state, { type: 'cancel', pointerId: 8 })
|
|
76
|
-
const cancelled = transitionViewportInteraction(rejectedCancel.state, {
|
|
77
|
-
type: 'cancel',
|
|
78
|
-
pointerId: 7,
|
|
79
|
-
})
|
|
80
|
-
const restarted = reduceViewportInteraction(null, { type: 'begin', gesture })
|
|
81
|
-
const reset = transitionViewportInteraction(restarted, { type: 'reset' })
|
|
82
|
-
|
|
83
|
-
expect(rejectedCancel.accepted).toBe(false)
|
|
84
|
-
expect(rejectedCancel.state).not.toBeNull()
|
|
85
|
-
expect(cancelled.accepted).toBe(true)
|
|
86
|
-
expect(cancelled.state).toBeNull()
|
|
87
|
-
expect(reset.accepted).toBe(true)
|
|
88
|
-
expect(reset.state).toBeNull()
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('keeps events discriminated and does not mutate the begin input', () => {
|
|
92
|
-
const event: ViewportInteractionEvent = { type: 'begin', gesture }
|
|
93
|
-
const state = reduceViewportInteraction(null, event)
|
|
94
|
-
|
|
95
|
-
expect(state).not.toBeNull()
|
|
96
|
-
expect(state?.xDomain).toEqual([0, 100])
|
|
97
|
-
expect(state?.yDomains.track).toEqual([-1, 1])
|
|
98
|
-
expect(event.gesture.xDomain).toEqual([0, 100])
|
|
99
|
-
expect(event.gesture.yDomains.track).toEqual([-1, 1])
|
|
100
|
-
})
|
|
101
|
-
})
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { useAnimationFrameThrottle } from './useAnimationFrameThrottle'
|
|
4
|
-
|
|
5
|
-
describe('useAnimationFrameThrottle', () => {
|
|
6
|
-
it('schedules callback on next animation frame', () => {
|
|
7
|
-
const throttle = useAnimationFrameThrottle()
|
|
8
|
-
const callback = vi.fn()
|
|
9
|
-
|
|
10
|
-
throttle.schedule(callback)
|
|
11
|
-
expect(throttle.isPending()).toBe(true)
|
|
12
|
-
expect(callback).not.toHaveBeenCalled()
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('replaces pending callback when scheduled multiple times', () => {
|
|
16
|
-
const throttle = useAnimationFrameThrottle()
|
|
17
|
-
const callback1 = vi.fn()
|
|
18
|
-
const callback2 = vi.fn()
|
|
19
|
-
|
|
20
|
-
throttle.schedule(callback1)
|
|
21
|
-
throttle.schedule(callback2)
|
|
22
|
-
expect(throttle.isPending()).toBe(true)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('cancels pending callback', () => {
|
|
26
|
-
const throttle = useAnimationFrameThrottle()
|
|
27
|
-
const callback = vi.fn()
|
|
28
|
-
|
|
29
|
-
throttle.schedule(callback)
|
|
30
|
-
throttle.cancel()
|
|
31
|
-
expect(throttle.isPending()).toBe(false)
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('flushes callback immediately', () => {
|
|
35
|
-
const throttle = useAnimationFrameThrottle()
|
|
36
|
-
const callback = vi.fn(() => 'result')
|
|
37
|
-
|
|
38
|
-
throttle.schedule(callback)
|
|
39
|
-
throttle.flush()
|
|
40
|
-
expect(callback).toHaveBeenCalled()
|
|
41
|
-
expect(throttle.isPending()).toBe(false)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('handles flush when no callback is pending', () => {
|
|
45
|
-
const throttle = useAnimationFrameThrottle()
|
|
46
|
-
expect(() => throttle.flush()).not.toThrow()
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('handles cancel when no callback is pending', () => {
|
|
50
|
-
const throttle = useAnimationFrameThrottle()
|
|
51
|
-
expect(() => throttle.cancel()).not.toThrow()
|
|
52
|
-
})
|
|
53
|
-
})
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import { DOMWrapper, flushPromises } from '@vue/test-utils'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
|
|
4
|
-
import { flushAnimationFrames } from '../../test/setup'
|
|
5
|
-
|
|
6
|
-
import { mountSizedChart } from '../../test/waveformChart'
|
|
7
|
-
|
|
8
|
-
function getAnnotationEditor() {
|
|
9
|
-
const editor = Array.from(document.body.querySelectorAll<HTMLElement>('[role="dialog"]'))
|
|
10
|
-
.filter((element) => element.closest('.waveform-annotation-editor'))
|
|
11
|
-
.at(-1)
|
|
12
|
-
if (!editor) throw new Error('Expected annotation editor modal to be mounted')
|
|
13
|
-
return new DOMWrapper(editor)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
describe('WaveformChart', () => {
|
|
17
|
-
it('edits and immediately deletes existing annotations without mutating props', async () => {
|
|
18
|
-
const sourceAnnotation = {
|
|
19
|
-
id: 'note',
|
|
20
|
-
seriesId: 'series-0',
|
|
21
|
-
x: 1,
|
|
22
|
-
y: 5,
|
|
23
|
-
text: '原文字',
|
|
24
|
-
}
|
|
25
|
-
const wrapper = await mountSizedChart(
|
|
26
|
-
{
|
|
27
|
-
kind: 'points',
|
|
28
|
-
points: [
|
|
29
|
-
{ x: 0, y: 0 },
|
|
30
|
-
{ x: 1, y: 5 },
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
{ annotations: [sourceAnnotation] },
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
await wrapper.get('[data-annotation-id="note"]').trigger('contextmenu', {
|
|
37
|
-
clientX: 200,
|
|
38
|
-
clientY: 100,
|
|
39
|
-
})
|
|
40
|
-
await wrapper.get('.waveform-annotation-context-menu button').trigger('click')
|
|
41
|
-
await flushPromises()
|
|
42
|
-
const editor = getAnnotationEditor()
|
|
43
|
-
await editor.get('textarea[aria-label="标注文本"]').setValue('新文字')
|
|
44
|
-
await editor.get('button.ant-btn-primary').trigger('click')
|
|
45
|
-
await flushPromises()
|
|
46
|
-
|
|
47
|
-
const updated = wrapper.emitted('update:annotations')?.at(-1)?.[0] as
|
|
48
|
-
Array<{ text: string }> | undefined
|
|
49
|
-
expect(updated?.[0].text).toBe('新文字')
|
|
50
|
-
expect(sourceAnnotation.text).toBe('原文字')
|
|
51
|
-
expect(wrapper.emitted('annotation-update')).toHaveLength(1)
|
|
52
|
-
|
|
53
|
-
await wrapper.get('[data-annotation-id="note"]').trigger('contextmenu', {
|
|
54
|
-
clientX: 200,
|
|
55
|
-
clientY: 100,
|
|
56
|
-
})
|
|
57
|
-
await wrapper.findAll('.waveform-annotation-context-menu button')[1].trigger('click')
|
|
58
|
-
expect(wrapper.emitted('update:annotations')?.at(-1)?.[0]).toEqual([])
|
|
59
|
-
expect(wrapper.emitted('annotation-delete')).toHaveLength(1)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('commits a dragged label offset once without changing its data anchor', async () => {
|
|
63
|
-
const wrapper = await mountSizedChart(
|
|
64
|
-
{
|
|
65
|
-
kind: 'points',
|
|
66
|
-
points: [
|
|
67
|
-
{ x: 0, y: 0 },
|
|
68
|
-
{ x: 1, y: 5 },
|
|
69
|
-
],
|
|
70
|
-
},
|
|
71
|
-
{ annotations: [{ id: 'dragged', seriesId: 'series-0', x: 1, y: 5, text: '拖动' }] },
|
|
72
|
-
)
|
|
73
|
-
const annotation = wrapper.get('[data-annotation-id="dragged"]')
|
|
74
|
-
const element = annotation.element as SVGElement & {
|
|
75
|
-
setPointerCapture: (pointerId: number) => void
|
|
76
|
-
releasePointerCapture: (pointerId: number) => void
|
|
77
|
-
hasPointerCapture: (pointerId: number) => boolean
|
|
78
|
-
}
|
|
79
|
-
element.setPointerCapture = () => undefined
|
|
80
|
-
element.releasePointerCapture = () => undefined
|
|
81
|
-
element.hasPointerCapture = () => false
|
|
82
|
-
|
|
83
|
-
const dispatchPointer = (type: string, values: Record<string, number>) => {
|
|
84
|
-
const event = new Event(type, { bubbles: true })
|
|
85
|
-
Object.defineProperties(event, {
|
|
86
|
-
button: { value: values.button ?? 0 },
|
|
87
|
-
clientX: { value: values.clientX ?? 0 },
|
|
88
|
-
clientY: { value: values.clientY ?? 0 },
|
|
89
|
-
pointerId: { value: values.pointerId ?? 1 },
|
|
90
|
-
})
|
|
91
|
-
element.dispatchEvent(event)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
dispatchPointer('pointerdown', { button: 0, clientX: 100, clientY: 100, pointerId: 1 })
|
|
95
|
-
expect(wrapper.emitted('update:annotations')).toBeUndefined()
|
|
96
|
-
dispatchPointer('pointermove', { clientX: 130, clientY: 120, pointerId: 1 })
|
|
97
|
-
flushAnimationFrames()
|
|
98
|
-
await flushPromises()
|
|
99
|
-
expect(wrapper.emitted('update:annotations')).toBeUndefined()
|
|
100
|
-
dispatchPointer('pointermove', { clientX: 140, clientY: 130, pointerId: 1 })
|
|
101
|
-
flushAnimationFrames()
|
|
102
|
-
await flushPromises()
|
|
103
|
-
expect(wrapper.emitted('update:annotations')).toBeUndefined()
|
|
104
|
-
const boxBeforeUp = {
|
|
105
|
-
x: wrapper.get('.waveform-annotation__box').attributes('x'),
|
|
106
|
-
y: wrapper.get('.waveform-annotation__box').attributes('y'),
|
|
107
|
-
}
|
|
108
|
-
dispatchPointer('pointerup', { clientX: 140, clientY: 130, pointerId: 1 })
|
|
109
|
-
await flushPromises()
|
|
110
|
-
expect(wrapper.get('.waveform-annotation__box').attributes('x')).toBe(boxBeforeUp.x)
|
|
111
|
-
expect(wrapper.get('.waveform-annotation__box').attributes('y')).toBe(boxBeforeUp.y)
|
|
112
|
-
|
|
113
|
-
const updated = wrapper.emitted('update:annotations')?.at(-1)?.[0] as
|
|
114
|
-
Array<{ x: number; y: number; labelOffsetX?: number; labelOffsetY?: number }> | undefined
|
|
115
|
-
expect(updated).toMatchObject([{ x: 1, y: 5, labelOffsetX: 40, labelOffsetY: 30 }])
|
|
116
|
-
expect(wrapper.emitted('update:annotations')).toHaveLength(1)
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('hides the tooltip through a label drag until the next real hover move', async () => {
|
|
120
|
-
const wrapper = await mountSizedChart(
|
|
121
|
-
{
|
|
122
|
-
kind: 'points',
|
|
123
|
-
points: [
|
|
124
|
-
{ x: 0, y: 0 },
|
|
125
|
-
{ x: 1, y: 5 },
|
|
126
|
-
],
|
|
127
|
-
},
|
|
128
|
-
{ annotations: [{ id: 'dragged', seriesId: 'series-0', x: 1, y: 5, text: '拖动' }] },
|
|
129
|
-
)
|
|
130
|
-
const overlay = wrapper.get('.waveform-chart__overlay')
|
|
131
|
-
const overlayWidth = Number(overlay.attributes('width'))
|
|
132
|
-
Object.defineProperty(overlay.element, 'getBoundingClientRect', {
|
|
133
|
-
value: () => ({ left: 0, top: 0, width: overlayWidth, height: 290 }),
|
|
134
|
-
})
|
|
135
|
-
overlay.element.dispatchEvent(
|
|
136
|
-
new MouseEvent('pointermove', { clientX: overlayWidth, clientY: 120, bubbles: true }),
|
|
137
|
-
)
|
|
138
|
-
flushAnimationFrames()
|
|
139
|
-
await flushPromises()
|
|
140
|
-
expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(true)
|
|
141
|
-
|
|
142
|
-
const annotation = wrapper.get('[data-annotation-id="dragged"]')
|
|
143
|
-
const element = annotation.element as SVGElement & {
|
|
144
|
-
setPointerCapture: (pointerId: number) => void
|
|
145
|
-
releasePointerCapture: (pointerId: number) => void
|
|
146
|
-
hasPointerCapture: (pointerId: number) => boolean
|
|
147
|
-
}
|
|
148
|
-
element.setPointerCapture = () => undefined
|
|
149
|
-
element.releasePointerCapture = () => undefined
|
|
150
|
-
element.hasPointerCapture = () => false
|
|
151
|
-
const dispatchPointer = (type: string, values: Record<string, number>) => {
|
|
152
|
-
const event = new Event(type, { bubbles: true })
|
|
153
|
-
Object.defineProperties(event, {
|
|
154
|
-
button: { value: values.button ?? 0 },
|
|
155
|
-
clientX: { value: values.clientX ?? 0 },
|
|
156
|
-
clientY: { value: values.clientY ?? 0 },
|
|
157
|
-
pointerId: { value: values.pointerId ?? 1 },
|
|
158
|
-
})
|
|
159
|
-
element.dispatchEvent(event)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
dispatchPointer('pointerdown', { button: 0, clientX: 100, clientY: 100, pointerId: 1 })
|
|
163
|
-
await flushPromises()
|
|
164
|
-
expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(false)
|
|
165
|
-
dispatchPointer('pointermove', { clientX: 130, clientY: 120, pointerId: 1 })
|
|
166
|
-
dispatchPointer('pointerup', { clientX: 130, clientY: 120, pointerId: 1 })
|
|
167
|
-
await flushPromises()
|
|
168
|
-
expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(false)
|
|
169
|
-
|
|
170
|
-
overlay.element.dispatchEvent(
|
|
171
|
-
new MouseEvent('pointermove', { clientX: overlayWidth / 2, clientY: 120, bubbles: true }),
|
|
172
|
-
)
|
|
173
|
-
flushAnimationFrames()
|
|
174
|
-
await flushPromises()
|
|
175
|
-
expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(false)
|
|
176
|
-
|
|
177
|
-
overlay.element.dispatchEvent(
|
|
178
|
-
new MouseEvent('pointermove', { clientX: overlayWidth, clientY: 120, bubbles: true }),
|
|
179
|
-
)
|
|
180
|
-
flushAnimationFrames()
|
|
181
|
-
await flushPromises()
|
|
182
|
-
expect(wrapper.find('.waveform-chart__tooltip').exists()).toBe(true)
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
it('controls visibility and interaction mode while filtering unknown series', async () => {
|
|
186
|
-
const wrapper = await mountSizedChart(
|
|
187
|
-
{
|
|
188
|
-
kind: 'points',
|
|
189
|
-
points: [
|
|
190
|
-
{ x: 0, y: 0 },
|
|
191
|
-
{ x: 1, y: 5 },
|
|
192
|
-
],
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
interactionMode: 'annotation',
|
|
196
|
-
annotations: [
|
|
197
|
-
{ id: 'valid', seriesId: 'series-0', x: 1, y: 5, text: '显示' },
|
|
198
|
-
{ id: 'unknown', seriesId: 'missing', x: 1, y: 5, text: '不显示' },
|
|
199
|
-
],
|
|
200
|
-
},
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
expect(wrapper.attributes('data-interaction-mode')).toBe('annotation')
|
|
204
|
-
expect(wrapper.findAll('.waveform-annotation')).toHaveLength(1)
|
|
205
|
-
expect(wrapper.find('.waveform-annotation-toolbar').exists()).toBe(false)
|
|
206
|
-
expect(wrapper.get('.waveform-chart__overlay').classes()).toContain('is-annotating')
|
|
207
|
-
await wrapper.setProps({ annotationsVisible: false })
|
|
208
|
-
expect(wrapper.find('.waveform-annotation').exists()).toBe(false)
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
it('reprojects annotations after zoom and keeps them in every display mode', async () => {
|
|
212
|
-
const wrapper = await mountSizedChart(
|
|
213
|
-
{
|
|
214
|
-
kind: 'series',
|
|
215
|
-
series: [
|
|
216
|
-
{
|
|
217
|
-
id: 'a',
|
|
218
|
-
name: 'A',
|
|
219
|
-
data: {
|
|
220
|
-
kind: 'points',
|
|
221
|
-
points: [
|
|
222
|
-
{ x: 0, y: 0 },
|
|
223
|
-
{ x: 1, y: 1 },
|
|
224
|
-
{ x: 2, y: 0 },
|
|
225
|
-
],
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
},
|
|
230
|
-
{ annotations: [{ id: 'note', seriesId: 'a', x: 1, y: 1, text: '峰值' }] },
|
|
231
|
-
)
|
|
232
|
-
const overlay = wrapper.get('.waveform-chart__overlay')
|
|
233
|
-
Object.defineProperty(overlay.element, 'getBoundingClientRect', {
|
|
234
|
-
value: () => ({ left: 0, top: 0, width: 712, height: 290 }),
|
|
235
|
-
})
|
|
236
|
-
const initialX = wrapper.get('.waveform-annotation__arrow').attributes('x2')
|
|
237
|
-
|
|
238
|
-
overlay.element.dispatchEvent(
|
|
239
|
-
new WheelEvent('wheel', {
|
|
240
|
-
deltaY: -200,
|
|
241
|
-
clientX: 600,
|
|
242
|
-
clientY: 145,
|
|
243
|
-
bubbles: true,
|
|
244
|
-
cancelable: true,
|
|
245
|
-
}),
|
|
246
|
-
)
|
|
247
|
-
flushAnimationFrames()
|
|
248
|
-
await flushPromises()
|
|
249
|
-
expect(wrapper.get('.waveform-annotation__arrow').attributes('x2')).not.toBe(initialX)
|
|
250
|
-
|
|
251
|
-
for (const displayMode of ['separated', 'compact'] as const) {
|
|
252
|
-
await wrapper.setProps({ displayMode })
|
|
253
|
-
await flushPromises()
|
|
254
|
-
expect(wrapper.find('[data-annotation-id="note"]').exists()).toBe(true)
|
|
255
|
-
}
|
|
256
|
-
})
|
|
257
|
-
})
|