Bug fix for dygraph point selection touch event.
[dygraphs.git] / auto_tests / tests / interaction_model.js
1 /**
2 * @fileoverview Test cases for the interaction model.
3 *
4 * @author konigsberg@google.com (Robert Konigsbrg)
5 */
6
7 import Dygraph from '../../src/dygraph';
8 import DygraphInteraction from '../../src/dygraph-interaction-model';
9 import DygraphOps from './DygraphOps';
10
11 describe("interaction-model", function() {
12
13 cleanupAfterEach();
14
15 var data1 = "X,Y\n" +
16 "20,-1\n" +
17 "21,0\n" +
18 "22,1\n" +
19 "23,0\n";
20
21 var data2 =
22 "X,Y\n" +
23 "1,10\n" +
24 "2,20\n" +
25 "3,30\n" +
26 "4,40\n" +
27 "5,120\n" +
28 "6,50\n" +
29 "7,70\n" +
30 "8,90\n" +
31 "9,50\n";
32
33 function getXLabels() {
34 var x_labels = document.getElementsByClassName("dygraph-axis-label-x");
35 var ary = [];
36 for (var i = 0; i < x_labels.length; i++) {
37 ary.push(x_labels[i].innerHTML);
38 }
39 return ary;
40 }
41
42 /*
43 it('testPan', function() {
44 var originalXRange = g.xAxisRange();
45 var originalYRange = g.yAxisRange(0);
46
47 DygraphOps.dispatchMouseDown(g, xRange[0], yRange[0]);
48 DygraphOps.dispatchMouseMove(g, xRange[1], yRange[0]); // this is really necessary.
49 DygraphOps.dispatchMouseUp(g, xRange[1], yRange[0]);
50
51 assert.closeTo(xRange, g.xAxisRange(), 0.2);
52 // assert.closeTo(originalYRange, g.yAxisRange(0), 0.2); // Not true, it's something in the middle.
53
54 var midX = (xRange[1] - xRange[0]) / 2;
55 DygraphOps.dispatchMouseDown(g, midX, yRange[0]);
56 DygraphOps.dispatchMouseMove(g, midX, yRange[1]); // this is really necessary.
57 DygraphOps.dispatchMouseUp(g, midX, yRange[1]);
58
59 assert.closeTo(xRange, g.xAxisRange(), 0.2);
60 assert.closeTo(yRange, g.yAxisRange(0), 0.2);
61 });
62 */
63
64 /**
65 * This tests that when changing the interaction model so pan is used instead
66 * of zoom as the default behavior, a standard click method is still called.
67 */
68 it('testClickCallbackIsCalled', function() {
69 var clicked;
70
71 var clickCallback = function(event, x) {
72 clicked = x;
73 };
74
75 var graph = document.getElementById("graph");
76 var g = new Dygraph(graph, data1,
77 {
78 width: 100,
79 height : 100,
80 clickCallback : clickCallback
81 });
82
83 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
84 DygraphOps.dispatchMouseMove_Point(g, 10, 10);
85 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
86
87 assert.equal(20, clicked);
88 });
89
90 /**
91 * This tests that when changing the interaction model so pan is used instead
92 * of zoom as the default behavior, a standard click method is still called.
93 */
94 it('testClickCallbackIsCalledOnCustomPan', function() {
95 var clicked;
96
97 var clickCallback = function(event, x) {
98 clicked = x;
99 };
100
101 function customDown(event, g, context) {
102 context.initializeMouseDown(event, g, context);
103 DygraphInteraction.startPan(event, g, context);
104 }
105
106 function customMove(event, g, context) {
107 DygraphInteraction.movePan(event, g, context);
108 }
109
110 function customUp(event, g, context) {
111 DygraphInteraction.endPan(event, g, context);
112 }
113
114 var opts = {
115 width: 100,
116 height : 100,
117 clickCallback : clickCallback,
118 interactionModel : {
119 'mousedown' : customDown,
120 'mousemove' : customMove,
121 'mouseup' : customUp,
122 }
123 };
124
125 var graph = document.getElementById("graph");
126 var g = new Dygraph(graph, data1, opts);
127
128 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
129 DygraphOps.dispatchMouseMove_Point(g, 10, 10);
130 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
131
132 assert.equal(20, clicked);
133 });
134
135 var clickAt = function(g, x, y) {
136 DygraphOps.dispatchMouseDown(g, x, y);
137 DygraphOps.dispatchMouseMove(g, x, y);
138 DygraphOps.dispatchMouseUp(g, x, y);
139 };
140
141 /**
142 * This tests that clickCallback is still called with the nonInteractiveModel.
143 */
144 it('testClickCallbackIsCalledWithNonInteractiveModel', function() {
145 var clicked;
146
147 // TODO(danvk): also test pointClickCallback here.
148 var clickCallback = function(event, x) {
149 clicked = x;
150 };
151
152 var opts = {
153 width: 100,
154 height : 100,
155 clickCallback : clickCallback,
156 interactionModel : DygraphInteraction.nonInteractiveModel_
157 };
158
159 var graph = document.getElementById("graph");
160 var g = new Dygraph(graph, data1, opts);
161
162 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
163 DygraphOps.dispatchMouseMove_Point(g, 10, 10);
164 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
165
166 assert.equal(20, clicked);
167 });
168
169 /**
170 * A sanity test to ensure pointClickCallback is called.
171 */
172 it('testPointClickCallback', function() {
173 var clicked = null;
174 var g = new Dygraph('graph', data2, {
175 pointClickCallback: function(event, point) {
176 clicked = point;
177 }
178 });
179
180 clickAt(g, 4, 40);
181
182 assert.isNotNull(clicked);
183 assert.equal(4, clicked.xval);
184 assert.equal(40, clicked.yval);
185 });
186
187 /**
188 * A sanity test to ensure pointClickCallback is not called when out of range.
189 */
190 it('testNoPointClickCallbackWhenOffPoint', function() {
191 var clicked;
192 var g = new Dygraph(document.getElementById("graph"), data2, {
193 pointClickCallback : function(event, point) {
194 clicked = point;
195 }
196 });
197
198 clickAt(g, 5, 40);
199
200 assert.isUndefined(clicked);
201 });
202
203 /**
204 * Ensures pointClickCallback circle size is taken into account.
205 */
206 it('testPointClickCallback_circleSize', function() {
207 // TODO(konigsberg): Implement.
208 });
209
210 /**
211 * Ensures that pointClickCallback is called prior to clickCallback
212 */
213 it('testPointClickCallbackCalledPriorToClickCallback', function() {
214 var counter = 0;
215 var pointClicked;
216 var clicked;
217 var g = new Dygraph(document.getElementById("graph"), data2, {
218 pointClickCallback : function(event, point) {
219 counter++;
220 pointClicked = counter;
221 },
222 clickCallback : function(event, point) {
223 counter++;
224 clicked = counter;
225 }
226 });
227
228 clickAt(g, 4, 40);
229 assert.equal(1, pointClicked);
230 assert.equal(2, clicked);
231 });
232
233 /**
234 * Ensures that when there's no pointClickCallback, clicking on a point still calls
235 * clickCallback
236 */
237 it('testClickCallback_clickOnPoint', function() {
238 var clicked;
239 var g = new Dygraph(document.getElementById("graph"), data2, {
240 clickCallback : function(event, point) {
241 clicked = 1;
242 }
243 });
244
245 clickAt(g, 4, 40);
246 assert.equal(1, clicked);
247 });
248
249 it('testIsZoomed_none', function() {
250 var g = new Dygraph(document.getElementById("graph"), data2, {});
251
252 assert.isFalse(g.isZoomed());
253 assert.isFalse(g.isZoomed("x"));
254 assert.isFalse(g.isZoomed("y"));
255 });
256
257 it('testIsZoomed_x', function() {
258 var g = new Dygraph(document.getElementById("graph"), data2, {});
259
260 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
261 DygraphOps.dispatchMouseMove_Point(g, 130, 100);
262 DygraphOps.dispatchMouseUp_Point(g, 130, 100);
263
264 assert.isTrue(g.isZoomed());
265 assert.isTrue(g.isZoomed("x"));
266 assert.isFalse(g.isZoomed("y"));
267 });
268
269 it('testIsZoomed_y', function() {
270 var g = new Dygraph(document.getElementById("graph"), data2, {});
271
272 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
273 DygraphOps.dispatchMouseMove_Point(g, 10, 30);
274 DygraphOps.dispatchMouseUp_Point(g, 10, 30);
275
276 assert.isTrue(g.isZoomed());
277 assert.isFalse(g.isZoomed("x"));
278 assert.isTrue(g.isZoomed("y"));
279 });
280
281 it('testIsZoomed_both', function() {
282 var g = new Dygraph(document.getElementById("graph"), data2, {});
283
284 // Zoom x axis
285 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
286 DygraphOps.dispatchMouseMove_Point(g, 130, 100);
287 DygraphOps.dispatchMouseUp_Point(g, 130, 100);
288
289 // Now zoom y axis
290 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
291 DygraphOps.dispatchMouseMove_Point(g, 100, 130);
292 DygraphOps.dispatchMouseUp_Point(g, 100, 130);
293
294
295 assert.isTrue(g.isZoomed());
296 assert.isTrue(g.isZoomed("x"));
297 assert.isTrue(g.isZoomed("y"));
298 });
299
300 it('testIsZoomed_updateOptions_none', function() {
301 var g = new Dygraph(document.getElementById("graph"), data2, {});
302
303 g.updateOptions({});
304
305 assert.isFalse(g.isZoomed());
306 assert.isFalse(g.isZoomed("x"));
307 assert.isFalse(g.isZoomed("y"));
308 });
309
310 it('testIsZoomed_updateOptions_x', function() {
311 var g = new Dygraph(document.getElementById("graph"), data2, {});
312
313 g.updateOptions({dateWindow: [-.5, .3]});
314 assert.isTrue(g.isZoomed());
315 assert.isTrue(g.isZoomed("x"));
316 assert.isFalse(g.isZoomed("y"));
317 });
318
319 it('testIsZoomed_updateOptions_y', function() {
320 var g = new Dygraph(document.getElementById("graph"), data2, {});
321
322 g.updateOptions({valueRange: [1, 10]});
323
324 assert.isTrue(g.isZoomed());
325 assert.isFalse(g.isZoomed("x"));
326 assert.isTrue(g.isZoomed("y"));
327 });
328
329 it('testIsZoomed_updateOptions_both', function() {
330 var g = new Dygraph(document.getElementById("graph"), data2, {});
331
332 g.updateOptions({dateWindow: [-1, 1], valueRange: [1, 10]});
333
334 assert.isTrue(g.isZoomed());
335 assert.isTrue(g.isZoomed("x"));
336 assert.isTrue(g.isZoomed("y"));
337 });
338
339
340 it('testCorrectAxisValueRangeAfterUnzoom', function() {
341 var g = new Dygraph(document.getElementById("graph"),
342 data2, {
343 valueRange: [1, 50],
344 dateWindow: [1, 9],
345 animatedZooms:false
346 });
347
348 // Zoom x axis
349 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
350 DygraphOps.dispatchMouseMove_Point(g, 130, 100);
351 DygraphOps.dispatchMouseUp_Point(g, 130, 100);
352
353 // Zoom y axis
354 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
355 DygraphOps.dispatchMouseMove_Point(g, 100, 130);
356 DygraphOps.dispatchMouseUp_Point(g, 100, 130);
357 var currentYAxisRange = g.yAxisRange();
358 var currentXAxisRange = g.xAxisRange();
359
360 //check that the range for the axis has changed
361 assert.notEqual(1, currentXAxisRange[0]);
362 assert.notEqual(10, currentXAxisRange[1]);
363 assert.notEqual(1, currentYAxisRange[0]);
364 assert.notEqual(50, currentYAxisRange[1]);
365
366 // unzoom by doubleclick. This is really the order in which a browser
367 // generates events, and we depend on it.
368 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
369 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
370 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
371 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
372 DygraphOps.dispatchDoubleClick(g, null);
373
374 // check if the range for both axis was reset to show the full data.
375 assert.deepEqual(g.yAxisExtremes()[0], g.yAxisRange());
376 assert.deepEqual(g.xAxisExtremes(), g.xAxisRange());
377 });
378
379 /**
380 * Ensures pointClickCallback is called when some points along the y-axis don't
381 * exist.
382 */
383 it('testPointClickCallback_missingData', function() {
384
385 // There's a B-value at 2, but no A-value.
386 var data =
387 "X,A,B\n" +
388 "1,,100\n"+
389 "2,,110\n"+
390 "3,140,120\n"+
391 "4,130,110\n"+
392 "";
393
394 var clicked;
395 var g = new Dygraph(document.getElementById("graph"), data, {
396 pointClickCallback : function(event, point) {
397 clicked = point;
398 }
399 });
400
401 clickAt(g, 2, 110);
402
403 assert.equal(2, clicked.xval);
404 assert.equal(110, clicked.yval);
405 });
406
407 describe('animated zooms', function() {
408 var oldDuration;
409
410 before(function() {
411 oldDuration = Dygraph.ANIMATION_DURATION;
412 Dygraph.ANIMATION_DURATION = 100; // speed up the animation for testing
413 });
414 after(function() {
415 Dygraph.ANIMATION_DURATION = oldDuration;
416 });
417
418 it('should support animated zooms', function(done) {
419 var data =
420 "X,A,B\n" +
421 "1,120,100\n"+
422 "2,110,110\n"+
423 "3,140,120\n"+
424 "4,130,110\n";
425
426 var ranges = [];
427
428 var g = new Dygraph('graph', data, {
429 animatedZooms: true,
430 });
431
432 // updating the dateWindow does not result in an animation.
433 assert.deepEqual([1, 4], g.xAxisRange());
434 g.updateOptions({dateWindow: [2, 4]});
435 assert.deepEqual([2, 4], g.xAxisRange());
436
437 g.updateOptions({
438 // zoomCallback is called once when the animation is complete.
439 zoomCallback: function(xMin, xMax) {
440 assert.equal(1, xMin);
441 assert.equal(4, xMax);
442 assert.deepEqual([1, 4], g.xAxisRange());
443 done();
444 }
445 }, false);
446
447 // Zoom out -- resetZoom() _does_ produce an animation.
448 g.resetZoom();
449 assert.notDeepEqual([2, 4], g.xAxisRange()); // first frame is synchronous
450 assert.notDeepEqual([1, 4], g.xAxisRange());
451
452 // at this point control flow goes up to zoomCallback
453 });
454
455 });
456
457 //bulk copied from "testCorrectAxisValueRangeAfterUnzoom"
458 //tests if the xRangePad is taken into account after unzoom.
459 it('testCorrectAxisPaddingAfterUnzoom', function() {
460 var g = new Dygraph(document.getElementById("graph"),
461 data2, {
462 valueRange: [1, 50],
463 dateWindow: [1, 9],
464 xRangePad: 10,
465 animatedZooms:false
466 });
467
468 var xExtremes = g.xAxisExtremes();
469 var [ yExtremes ] = g.yAxisExtremes();
470
471 // Zoom x axis
472 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
473 DygraphOps.dispatchMouseMove_Point(g, 130, 100);
474 DygraphOps.dispatchMouseUp_Point(g, 130, 100);
475
476 // Zoom y axis
477 DygraphOps.dispatchMouseDown_Point(g, 100, 100);
478 DygraphOps.dispatchMouseMove_Point(g, 100, 130);
479 DygraphOps.dispatchMouseUp_Point(g, 100, 130);
480
481 //check that the range for the axis has changed
482 assert.notDeepEqual([1, 10], g.xAxisRange());
483 assert.notDeepEqual([1, 50], g.yAxisRange());
484
485 // unzoom by doubleclick. This is really the order in which a browser
486 // generates events, and we depend on it.
487 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
488 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
489 DygraphOps.dispatchMouseDown_Point(g, 10, 10);
490 DygraphOps.dispatchMouseUp_Point(g, 10, 10);
491 DygraphOps.dispatchDoubleClick(g, null);
492
493 // check if range for x-axis was reset to original value.
494 assert.deepEqual(xExtremes, g.xAxisRange());
495 assert.deepEqual(yExtremes, g.yAxisRange());
496 });
497
498 });