fix(chart): support wheel zoom out
All checks were successful
Package component / package (push) Successful in 3m50s
All checks were successful
Package component / package (push) Successful in 3m50s
This commit is contained in:
@@ -225,7 +225,7 @@ describe('WaveformChart', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('uses each track x domain in independent mode instead of the shared initial domain', async () => {
|
||||
it('uses the shared initial x domain for every independent track by default', async () => {
|
||||
const wrapper = await mountSizedChart(
|
||||
{
|
||||
kind: 'series',
|
||||
@@ -262,7 +262,9 @@ describe('WaveformChart', () => {
|
||||
)
|
||||
|
||||
const tracks = wrapper.findAll('.waveform-chart__track')
|
||||
expect(tracks[0]?.get('.waveform-chart__axis-endpoint--end').text()).toBe('6.00')
|
||||
expect(tracks[0]?.get('.waveform-chart__axis-endpoint--start').text()).toBe('-8.00')
|
||||
expect(tracks[0]?.get('.waveform-chart__axis-endpoint--end').text()).toBe('5.00')
|
||||
expect(tracks[1]?.get('.waveform-chart__axis-endpoint--start').text()).toBe('-8.00')
|
||||
expect(tracks[1]?.get('.waveform-chart__axis-endpoint--end').text()).toBe('5.00')
|
||||
})
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ describe('WaveformChart presentation mode', () => {
|
||||
await flushPromises()
|
||||
overlay.element.dispatchEvent(
|
||||
new WheelEvent('wheel', {
|
||||
deltaY: -200,
|
||||
deltaY: 200,
|
||||
clientX: width / 2,
|
||||
clientY: height / 2,
|
||||
bubbles: true,
|
||||
|
||||
@@ -38,6 +38,21 @@ describe('WaveformChart', () => {
|
||||
expect(domain[1]).toBeLessThanOrEqual(1)
|
||||
expect(endpoints()[0]).not.toBe(initialEndpoints[0])
|
||||
expect(endpoints()[1]).toBe(initialEndpoints[1])
|
||||
|
||||
overlays[0].element.dispatchEvent(
|
||||
new WheelEvent('wheel', {
|
||||
deltaY: 4_000,
|
||||
clientX: firstWidth - 1,
|
||||
clientY: 130,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
)
|
||||
flushAnimationFrames()
|
||||
await flushPromises()
|
||||
|
||||
expect(endpoints()[0]).toBe(initialEndpoints[0])
|
||||
expect(endpoints()[1]).toBe(initialEndpoints[1])
|
||||
})
|
||||
|
||||
it('emits one zoom-end payload after a shared zoom gesture completes', async () => {
|
||||
|
||||
@@ -207,7 +207,7 @@ describe('WaveformChart', () => {
|
||||
expect(secondDomain[1] - secondDomain[0]).toBeCloseTo(10)
|
||||
})
|
||||
|
||||
it('keeps one global domain while loading narrower and wider windows', async () => {
|
||||
it('zooms a shared viewport back out after loading a narrower data window', async () => {
|
||||
const wrapper = await mountSizedChart(
|
||||
{
|
||||
kind: 'points',
|
||||
@@ -256,9 +256,57 @@ describe('WaveformChart', () => {
|
||||
|
||||
const eventCount = wrapper.emitted('zoom-change')?.length ?? 0
|
||||
await dispatchWheel(4000)
|
||||
const currentDomain = wrapper.emitted('zoom-change')?.at(-1)?.[0] as [number, number]
|
||||
const zoomedOutDomain = wrapper.emitted('zoom-change')?.at(-1)?.[0] as [number, number]
|
||||
expect(wrapper.emitted('zoom-change')?.length ?? 0).toBe(eventCount + 1)
|
||||
expect(zoomedOutDomain[1] - zoomedOutDomain[0]).toBeCloseTo(100)
|
||||
|
||||
const fullDomainEventCount = wrapper.emitted('zoom-change')?.length ?? 0
|
||||
await dispatchWheel(4000)
|
||||
expect(wrapper.emitted('zoom-change')?.length ?? 0).toBe(fullDomainEventCount)
|
||||
})
|
||||
|
||||
it('allows zooming out when the minimum visible point count blocks zooming in', async () => {
|
||||
const wrapper = await mountSizedChart({
|
||||
kind: 'points',
|
||||
points: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 1, y: 10 },
|
||||
{ x: 2, y: 20 },
|
||||
],
|
||||
})
|
||||
const overlay = wrapper.get('.waveform-chart__overlay--independent')
|
||||
const width = Number(overlay.attributes('width'))
|
||||
Object.defineProperty(overlay.element, 'getBoundingClientRect', {
|
||||
value: () => ({ left: 0, top: 0, width, height: 290 }),
|
||||
})
|
||||
const dispatchWheel = async (deltaY: number) => {
|
||||
overlay.element.dispatchEvent(
|
||||
new WheelEvent('wheel', {
|
||||
deltaY,
|
||||
clientX: width / 2,
|
||||
clientY: 145,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
)
|
||||
flushAnimationFrames()
|
||||
await flushPromises()
|
||||
}
|
||||
|
||||
await dispatchWheel(-4000)
|
||||
const zoomedDomain = wrapper.emitted('zoom-change')?.at(-1)?.[0] as [number, number]
|
||||
expect(zoomedDomain[1] - zoomedDomain[0]).toBeCloseTo(0.05)
|
||||
|
||||
await wrapper.setProps({ minVisiblePoints: 10 })
|
||||
await flushPromises()
|
||||
const eventCount = wrapper.emitted('zoom-change')?.length ?? 0
|
||||
await dispatchWheel(-1000)
|
||||
expect(wrapper.emitted('zoom-change')?.length ?? 0).toBe(eventCount)
|
||||
expect(currentDomain[1] - currentDomain[0]).toBeCloseTo(2.5)
|
||||
|
||||
await dispatchWheel(4000)
|
||||
const zoomedOutDomain = wrapper.emitted('zoom-change')?.at(-1)?.[0] as [number, number]
|
||||
expect(wrapper.emitted('zoom-change')?.length ?? 0).toBe(eventCount + 1)
|
||||
expect(zoomedOutDomain).toEqual([0, 2])
|
||||
})
|
||||
|
||||
it('resets a shared viewport on double-click and emits zoom-reset', async () => {
|
||||
|
||||
114
src/components/waveformChartCases/wheelZoomOut.test.ts
Normal file
114
src/components/waveformChartCases/wheelZoomOut.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { flushPromises } from '@vue/test-utils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { flushAnimationFrames } from '../../test/setup'
|
||||
import { mountSizedChart } from '../../test/waveformChart'
|
||||
|
||||
describe('WaveformChart wheel zoom out', () => {
|
||||
it('restores an independent initial domain after replacing data with a narrow window', async () => {
|
||||
const createData = (start: number, end: number) => ({
|
||||
kind: 'series' as const,
|
||||
series: [
|
||||
{
|
||||
id: 'channel-a',
|
||||
name: '通道 A',
|
||||
data: {
|
||||
kind: 'points' as const,
|
||||
points: [
|
||||
{ x: start, y: 0 },
|
||||
{ x: end, y: 1 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
const wrapper = await mountSizedChart(createData(-5, 5), {
|
||||
displayMode: 'independent',
|
||||
initialXDomain: [-5, 5],
|
||||
})
|
||||
const overlay = wrapper.get('.waveform-chart__overlay--independent')
|
||||
const overlayWidth = Number(overlay.attributes('width'))
|
||||
Object.defineProperty(overlay.element, 'getBoundingClientRect', {
|
||||
value: () => ({ left: 0, top: 0, width: overlayWidth, height: 290 }),
|
||||
})
|
||||
const dispatchWheel = async (deltaY: number) => {
|
||||
overlay.element.dispatchEvent(
|
||||
new WheelEvent('wheel', {
|
||||
deltaY,
|
||||
clientX: overlayWidth / 2,
|
||||
clientY: 145,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
)
|
||||
flushAnimationFrames()
|
||||
await flushPromises()
|
||||
}
|
||||
const endpoints = () => ({
|
||||
start: wrapper.get('.waveform-chart__axis-endpoint--start').text(),
|
||||
end: wrapper.get('.waveform-chart__axis-endpoint--end').text(),
|
||||
})
|
||||
|
||||
expect(endpoints()).toEqual({ start: '-5.00', end: '5.00' })
|
||||
await dispatchWheel(-4000)
|
||||
expect(endpoints()).not.toEqual({ start: '-5.00', end: '5.00' })
|
||||
|
||||
await wrapper.setProps({ data: createData(-0.125, 0.125) })
|
||||
await flushPromises()
|
||||
await dispatchWheel(4000)
|
||||
|
||||
expect(endpoints()).toEqual({ start: '-5.00', end: '5.00' })
|
||||
})
|
||||
|
||||
it('emits one zoom-end payload after zooming a shared viewport out', async () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
const wrapper = await mountSizedChart({
|
||||
kind: 'points',
|
||||
points: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 2, y: 1 },
|
||||
],
|
||||
})
|
||||
const overlay = wrapper.get('.waveform-chart__overlay')
|
||||
const overlayWidth = Number(overlay.attributes('width'))
|
||||
Object.defineProperty(overlay.element, 'getBoundingClientRect', {
|
||||
value: () => ({ left: 0, top: 0, width: overlayWidth, height: 290 }),
|
||||
})
|
||||
const dispatchWheel = (deltaY: number) =>
|
||||
overlay.element.dispatchEvent(
|
||||
new WheelEvent('wheel', {
|
||||
deltaY,
|
||||
clientX: overlayWidth / 2,
|
||||
clientY: 145,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
)
|
||||
|
||||
dispatchWheel(-4000)
|
||||
flushAnimationFrames()
|
||||
await vi.advanceTimersByTimeAsync(200)
|
||||
await flushPromises()
|
||||
const zoomedDomain = wrapper.emitted('zoom-change')?.at(-1)?.[0] as [number, number]
|
||||
const zoomedSpan = zoomedDomain[1] - zoomedDomain[0]
|
||||
|
||||
dispatchWheel(1000)
|
||||
flushAnimationFrames()
|
||||
expect(wrapper.emitted('zoom-end')).toHaveLength(1)
|
||||
await vi.advanceTimersByTimeAsync(200)
|
||||
await flushPromises()
|
||||
|
||||
const zoomedOutPayload = wrapper.emitted('zoom-end')?.at(-1)?.[0] as {
|
||||
start: number
|
||||
end: number
|
||||
gesture: string
|
||||
}
|
||||
expect(wrapper.emitted('zoom-end')).toHaveLength(2)
|
||||
expect(zoomedOutPayload.end - zoomedOutPayload.start).toBeGreaterThan(zoomedSpan)
|
||||
expect(zoomedOutPayload.gesture).toBe('wheel')
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user