Skip to content

Commit c610f0a

Browse files
committed
Chart: prevent unexpected page scrolling on zoom (T1314606)
1 parent 04cd567 commit c610f0a

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const SCROLL_BAR_END_EVENT_NAME = 'dxc-scroll-end' + EVENTS_NS;
2424

2525
const GESTURE_TIMEOUT = 300;
2626
const MIN_DRAG_DELTA = 5;
27+
export const SCROLL_PREVENTION_TIMEOUT = 500;
2728

2829
const _min = Math.min;
2930
const _max = Math.max;
@@ -114,6 +115,7 @@ export default {
114115
init: function() {
115116
const chart = this;
116117
const renderer = this._renderer;
118+
let lastWheelTimer;
117119

118120
function getAxesCopy(zoomAndPan, actionField) {
119121
let axes = [];
@@ -286,13 +288,22 @@ export default {
286288
return e.offset[coordField] - actionData.offset[coordField];
287289
}
288290

289-
function preventDefaults(e) {
291+
function setLastWheelTimer() {
292+
clearTimeout(lastWheelTimer);
293+
lastWheelTimer = setTimeout(() => {
294+
lastWheelTimer = undefined;
295+
}, SCROLL_PREVENTION_TIMEOUT);
296+
}
297+
298+
function preventDefaults(e, stopChartHandler = true) {
290299
if(e.cancelable !== false) {
291300
e.preventDefault();
292301
e.stopPropagation();
293302
}
294303

295-
chart._stopCurrentHandling();
304+
if(stopChartHandler) {
305+
chart._stopCurrentHandling();
306+
}
296307
}
297308

298309
const zoomAndPan = {
@@ -496,12 +507,20 @@ export default {
496507
axesZoomed |= canZoom && zoomAxes(e, chart._argumentAxes, getRange, e.delta > 0, { coord: rotated ? coords.y : coords.x }, chart.getArgumentAxis());
497508
}
498509

510+
const isPanningAvailable = targetAxes ? isAxisAvailablePanning(targetAxes) : zoomAndPan.panningVisualRangeEnabled();
511+
499512
if(axesZoomed) {
500513
chart._requestChange(['VISUAL_RANGE']);
501-
if(targetAxes && isAxisAvailablePanning(targetAxes) || !targetAxes && zoomAndPan.panningVisualRangeEnabled()) {
514+
if(isPanningAvailable) {
502515
preventDefaults(e); // T249548
516+
setLastWheelTimer();
503517
}
504518
}
519+
520+
if((!axesZoomed || !isPanningAvailable) && lastWheelTimer) {
521+
preventDefaults(e, false);
522+
setLastWheelTimer();
523+
}
505524
},
506525
cleanup: function() {
507526
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';
@@ -3211,6 +3212,57 @@ QUnit.test('Default behavior - no prevent. On mouse wheel', function(assert) {
32113212
assert.equal(this.trackerStopHandling.callCount, 0);
32123213
});
32133214

3215+
QUnit.test('On mouse wheel. Should prevent scroll page after max zoom level reached (T1314606)', function(assert) {
3216+
const preventDefault = sinon.spy();
3217+
const stopPropagation = sinon.spy();
3218+
const onZoomEnd = sinon.spy();
3219+
const wholeRange = { startValue: 0, endValue: 5 };
3220+
const chart = this.createChart({
3221+
argumentAxis: {
3222+
visualRange: {
3223+
startValue: 0.1,
3224+
endValue: 4.9
3225+
},
3226+
wholeRange,
3227+
},
3228+
zoomAndPan: {
3229+
argumentAxis: 'zoom',
3230+
allowMouseWheel: true
3231+
},
3232+
onZoomEnd: onZoomEnd
3233+
});
3234+
3235+
const $root = $(chart._renderer.root.element);
3236+
3237+
$root.trigger(new $.Event('dxmousewheel', { d: -10, pageX: 200, pageY: 250, preventDefault: preventDefault, stopPropagation: stopPropagation }));
3238+
3239+
assert.deepEqual(onZoomEnd.getCall(0).args[0].range, wholeRange, 'chart zoomed out to wholeRange');
3240+
assert.strictEqual(preventDefault.callCount, 1, 'after zoom e.preventDefault called');
3241+
assert.strictEqual(stopPropagation.callCount, 1, 'after zoom e.stopPropagation called');
3242+
assert.strictEqual(this.trackerStopHandling.callCount, 1, 'chart stopped wheel event handling');
3243+
3244+
this.clock.tick(SCROLL_PREVENTION_TIMEOUT / 2);
3245+
$root.trigger(new $.Event('dxmousewheel', { d: -10, pageX: 200, pageY: 250, preventDefault: preventDefault, stopPropagation: stopPropagation }));
3246+
3247+
assert.equal(preventDefault.callCount, 2, 'e.preventDefault called');
3248+
assert.equal(stopPropagation.callCount, 2, 'e.stopPropagation called');
3249+
assert.equal(this.trackerStopHandling.callCount, 1, 'chart not passed event handling in SCROLL_PREVENTION_TIMEOUT window after zoom');
3250+
3251+
this.clock.tick(SCROLL_PREVENTION_TIMEOUT - 10);
3252+
$root.trigger(new $.Event('dxmousewheel', { d: -10, pageX: 200, pageY: 250, preventDefault: preventDefault, stopPropagation: stopPropagation }));
3253+
3254+
assert.equal(preventDefault.callCount, 3, 'e.preventDefault called');
3255+
assert.equal(stopPropagation.callCount, 3, 'e.stopPropagation called');
3256+
assert.equal(this.trackerStopHandling.callCount, 1, 'chart not passed event handling in SCROLL_PREVENTION_TIMEOUT window after last wheel event on chart');
3257+
3258+
this.clock.tick(SCROLL_PREVENTION_TIMEOUT + 10);
3259+
$root.trigger(new $.Event('dxmousewheel', { d: -10, pageX: 200, pageY: 250, preventDefault: preventDefault, stopPropagation: stopPropagation }));
3260+
3261+
assert.equal(preventDefault.callCount, 3, 'chart not prevents event handling after SCROLL_PREVENTION_TIMEOUT expires, e.preventDefault not called');
3262+
assert.equal(stopPropagation.callCount, 3, 'chart not prevent event propagation after SCROLL_PREVENTION_TIMEOUT expires, e.stopPropagation not called');
3263+
assert.equal(this.trackerStopHandling.callCount, 1, 'chart._stopCurrentHandling not called after SCROLL_PREVENTION_TIMEOUT window');
3264+
});
3265+
32143266
QUnit.test('On pinch zoom', function(assert) {
32153267
const preventDefault = sinon.spy();
32163268
const stopPropagation = sinon.spy();

0 commit comments

Comments
 (0)