Skip to content

Commit 67e3fc5

Browse files
Chart: prevent unexpected page scrolling on zoom (T1314606) (25_1) (#31871)
1 parent 79941e0 commit 67e3fc5

File tree

2 files changed

+75
-3
lines changed

2 files changed

+75
-3
lines changed

packages/devextreme/js/__internal/viz/chart_components/zoom_and_pan.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const SCROLL_BAR_END_EVENT_NAME = `dxc-scroll-end${EVENTS_NS}`;
3939

4040
const GESTURE_TIMEOUT = 300;
4141
const MIN_DRAG_DELTA = 5;
42+
export const SCROLL_PREVENTION_TIMEOUT = 500;
4243

4344
const _min = Math.min;
4445
const _max = Math.max;
@@ -129,6 +130,7 @@ export default {
129130
init() {
130131
const chart = this;
131132
const renderer = this._renderer;
133+
let lastWheelTimer: number | undefined;
132134

133135
function getAxesCopy(zoomAndPan, actionField) {
134136
let axes = [];
@@ -307,13 +309,23 @@ export default {
307309
return e.offset[coordField] - actionData.offset[coordField];
308310
}
309311

310-
function preventDefaults(e) {
312+
function setLastWheelTimer() {
313+
clearTimeout(lastWheelTimer);
314+
// eslint-disable-next-line no-restricted-globals
315+
lastWheelTimer = setTimeout(() => {
316+
lastWheelTimer = undefined;
317+
}, SCROLL_PREVENTION_TIMEOUT) as unknown as number;
318+
}
319+
320+
function preventDefaults(e, stopChartHandler = true): void {
311321
if (e.cancelable !== false) {
312322
e.preventDefault();
313323
e.stopPropagation();
314324
}
315325

316-
chart._stopCurrentHandling();
326+
if (stopChartHandler) {
327+
chart._stopCurrentHandling();
328+
}
317329
}
318330

319331
const zoomAndPan = {
@@ -536,12 +548,20 @@ export default {
536548
axesZoomed |= canZoom && zoomAxes(e, chart._argumentAxes, getRange, e.delta > 0, { coord: rotated ? coords.y : coords.x }, chart.getArgumentAxis());
537549
}
538550

551+
const isPanningAvailable = targetAxes ? isAxisAvailablePanning(targetAxes) : zoomAndPan.panningVisualRangeEnabled();
552+
539553
if (axesZoomed) {
540554
chart._requestChange(['VISUAL_RANGE']);
541-
if (targetAxes && isAxisAvailablePanning(targetAxes) || !targetAxes && zoomAndPan.panningVisualRangeEnabled()) {
555+
if (isPanningAvailable) {
542556
preventDefaults(e); // T249548
557+
setLastWheelTimer();
543558
}
544559
}
560+
561+
if ((!axesZoomed || !isPanningAvailable) && lastWheelTimer) {
562+
preventDefaults(e, false);
563+
setLastWheelTimer();
564+
}
545565
},
546566
cleanup() {
547567
renderer.root.off(EVENTS_NS);

packages/devextreme/testing/tests/DevExpress.viz.charts/zoomAndPan.tests.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import $ from 'jquery';
22
import pointerMock from '../../helpers/pointerMock.js';
33

44
import 'viz/chart';
5+
import { SCROLL_PREVENTION_TIMEOUT } from '__internal/viz/chart_components/zoom_and_pan';
56

67
const CHART_SVG_SELECTOR = 'svg.dxc.dxc-chart';
78
const TOOLTIP_CLASS = 'dxc-tooltip';
@@ -3109,6 +3110,57 @@ QUnit.test('Default behavior - no prevent. On mouse wheel', function(assert) {
31093110
assert.equal(this.trackerStopHandling.callCount, 0);
31103111
});
31113112

3113+
QUnit.test('On mouse wheel. Should prevent scroll page after max zoom level reached (T1314606)', function(assert) {
3114+
const preventDefault = sinon.spy();
3115+
const stopPropagation = sinon.spy();
3116+
const onZoomEnd = sinon.spy();
3117+
const wholeRange = { startValue: 0, endValue: 5 };
3118+
const chart = this.createChart({
3119+
argumentAxis: {
3120+
visualRange: {
3121+
startValue: 0.1,
3122+
endValue: 4.9
3123+
},
3124+
wholeRange,
3125+
},
3126+
zoomAndPan: {
3127+
argumentAxis: 'zoom',
3128+
allowMouseWheel: true
3129+
},
3130+
onZoomEnd: onZoomEnd
3131+
});
3132+
3133+
const $root = $(chart._renderer.root.element);
3134+
3135+
$root.trigger(new $.Event('dxmousewheel', { d: -10, pageX: 200, pageY: 250, preventDefault: preventDefault, stopPropagation: stopPropagation }));
3136+
3137+
assert.deepEqual(onZoomEnd.getCall(0).args[0].range, wholeRange, 'chart zoomed out to wholeRange');
3138+
assert.strictEqual(preventDefault.callCount, 1, 'after zoom e.preventDefault called');
3139+
assert.strictEqual(stopPropagation.callCount, 1, 'after zoom e.stopPropagation called');
3140+
assert.strictEqual(this.trackerStopHandling.callCount, 1, 'chart stopped wheel event handling');
3141+
3142+
this.clock.tick(SCROLL_PREVENTION_TIMEOUT / 2);
3143+
$root.trigger(new $.Event('dxmousewheel', { d: -10, pageX: 200, pageY: 250, preventDefault: preventDefault, stopPropagation: stopPropagation }));
3144+
3145+
assert.equal(preventDefault.callCount, 2, 'e.preventDefault called');
3146+
assert.equal(stopPropagation.callCount, 2, 'e.stopPropagation called');
3147+
assert.equal(this.trackerStopHandling.callCount, 1, 'chart not passed event handling in SCROLL_PREVENTION_TIMEOUT window after zoom');
3148+
3149+
this.clock.tick(SCROLL_PREVENTION_TIMEOUT - 10);
3150+
$root.trigger(new $.Event('dxmousewheel', { d: -10, pageX: 200, pageY: 250, preventDefault: preventDefault, stopPropagation: stopPropagation }));
3151+
3152+
assert.equal(preventDefault.callCount, 3, 'e.preventDefault called');
3153+
assert.equal(stopPropagation.callCount, 3, 'e.stopPropagation called');
3154+
assert.equal(this.trackerStopHandling.callCount, 1, 'chart not passed event handling in SCROLL_PREVENTION_TIMEOUT window after last wheel event on chart');
3155+
3156+
this.clock.tick(SCROLL_PREVENTION_TIMEOUT + 10);
3157+
$root.trigger(new $.Event('dxmousewheel', { d: -10, pageX: 200, pageY: 250, preventDefault: preventDefault, stopPropagation: stopPropagation }));
3158+
3159+
assert.equal(preventDefault.callCount, 3, 'chart not prevents event handling after SCROLL_PREVENTION_TIMEOUT expires, e.preventDefault not called');
3160+
assert.equal(stopPropagation.callCount, 3, 'chart not prevent event propagation after SCROLL_PREVENTION_TIMEOUT expires, e.stopPropagation not called');
3161+
assert.equal(this.trackerStopHandling.callCount, 1, 'chart._stopCurrentHandling not called after SCROLL_PREVENTION_TIMEOUT window');
3162+
});
3163+
31123164
QUnit.test('On pinch zoom', function(assert) {
31133165
const preventDefault = sinon.spy();
31143166
const stopPropagation = sinon.spy();

0 commit comments

Comments
 (0)