From 9c8314318c3f11f2d3e26ce6e67f1ac07841ca93 Mon Sep 17 00:00:00 2001 From: Robert Konigsberg Date: Fri, 13 May 2011 11:18:09 -0400 Subject: [PATCH] Start adding tests for the interaction model. Create the beginning of a solution for getting clickCallbacks when the interaction model is rewritten. There's lots about this change I dislike (the method naming for one.) Still pending is the lack of maintaining lastx and selPoints. --- auto_tests/misc/local.html | 1 + auto_tests/tests/DygraphOps.js | 54 +++++++++----- auto_tests/tests/interaction_model.js | 128 ++++++++++++++++++++++++++++++++++ auto_tests/tests/range_tests.js | 1 - dygraph.js | 63 ++++++++++------- 5 files changed, 202 insertions(+), 45 deletions(-) create mode 100644 auto_tests/tests/interaction_model.js diff --git a/auto_tests/misc/local.html b/auto_tests/misc/local.html index 046a11e..32814be 100644 --- a/auto_tests/misc/local.html +++ b/auto_tests/misc/local.html @@ -24,6 +24,7 @@ +
diff --git a/auto_tests/tests/DygraphOps.js b/auto_tests/tests/DygraphOps.js index 072d992..ad67b2a 100644 --- a/auto_tests/tests/DygraphOps.js +++ b/auto_tests/tests/DygraphOps.js @@ -90,12 +90,9 @@ DygraphOps.dispatchDoubleClick = function(g, custom) { g.canvas_.dispatchEvent(event); }; -DygraphOps.dispatchMouseDown = function(g, x, y, custom) { - var px = Dygraph.findPosX(g.canvas_); - var py = Dygraph.findPosY(g.canvas_); - - var pageX = px + g.toDomXCoord(x); - var pageY = py + g.toDomYCoord(y); +DygraphOps.dispatchMouseDown_Point = function(g, x, y, custom) { + var pageX = Dygraph.findPosX(g.canvas_) + x; + var pageY = Dygraph.findPosY(g.canvas_) + y; var opts = { type : 'mousedown', @@ -108,14 +105,11 @@ DygraphOps.dispatchMouseDown = function(g, x, y, custom) { var event = DygraphOps.createEvent_(opts, custom); g.canvas_.dispatchEvent(event); -}; - -DygraphOps.dispatchMouseMove = function(g, x, y, custom) { - var px = Dygraph.findPosX(g.canvas_); - var py = Dygraph.findPosY(g.canvas_); +} - var pageX = px + g.toDomXCoord(x); - var pageY = py + g.toDomYCoord(y); +DygraphOps.dispatchMouseMove_Point = function(g, x, y, custom) { + var pageX = Dygraph.findPosX(g.canvas_) + x; + var pageY = Dygraph.findPosY(g.canvas_) + y; var opts = { type : 'mousemove', @@ -129,12 +123,9 @@ DygraphOps.dispatchMouseMove = function(g, x, y, custom) { g.canvas_.dispatchEvent(event); }; -DygraphOps.dispatchMouseUp = function(g, x, y, custom) { - var px = Dygraph.findPosX(g.canvas_); - var py = Dygraph.findPosY(g.canvas_); - - var pageX = px + g.toDomXCoord(x); - var pageY = py + g.toDomYCoord(y); +DygraphOps.dispatchMouseUp_Point = function(g, x, y, custom) { + var pageX = Dygraph.findPosX(g.canvas_) + x; + var pageY = Dygraph.findPosY(g.canvas_) + y; var opts = { type : 'mouseup', @@ -147,3 +138,28 @@ DygraphOps.dispatchMouseUp = function(g, x, y, custom) { var event = DygraphOps.createEvent_(opts, custom); g.canvas_.dispatchEvent(event); }; + +DygraphOps.dispatchMouseDown = function(g, x, y, custom) { + DygraphOps.dispatchMouseDown_Point( + g, + g.toDomXCoord(x), + g.toDomYCoord(y), + custom); +}; + +DygraphOps.dispatchMouseMove = function(g, x, y, custom) { + DygraphOps.dispatchMouseMove_Point( + g, + g.toDomXCoord(x), + g.toDomYCoord(y), + custom); +}; + +DygraphOps.dispatchMouseUp = function(g, x, y, custom) { + DygraphOps.dispatchMouseUp_Point( + g, + g.toDomXCoord(x), + g.toDomYCoord(y), + custom); +}; + diff --git a/auto_tests/tests/interaction_model.js b/auto_tests/tests/interaction_model.js new file mode 100644 index 0000000..6249731 --- /dev/null +++ b/auto_tests/tests/interaction_model.js @@ -0,0 +1,128 @@ +/** + * @fileoverview Test cases for the interaction model. + * + * @author konigsberg@google.com (Robert Konigsbrg) + */ +var InteractionModelTestCase = TestCase("interaction-model"); + +InteractionModelTestCase.prototype.setUp = function() { + document.body.innerHTML = "
"; +}; + +InteractionModelTestCase.prototype.tearDown = function() { +}; + +function getXLabels() { + var x_labels = document.getElementsByClassName("dygraph-axis-label-x"); + var ary = []; + for (var i = 0; i < x_labels.length; i++) { + ary.push(x_labels[i].innerHTML); + } + return ary; +} + +InteractionModelTestCase.prototype.pan = function(g, xRange, yRange) { + var originalXRange = g.xAxisRange(); + var originalYRange = g.yAxisRange(0); + + DygraphOps.dispatchMouseDown(g, xRange[0], yRange[0]); + DygraphOps.dispatchMouseMove(g, xRange[1], yRange[0]); // this is really necessary. + DygraphOps.dispatchMouseUp(g, xRange[1], yRange[0]); + + assertEqualsDelta(xRange, g.xAxisRange(), 0.2); + // assertEqualsDelta(originalYRange, g.yAxisRange(0), 0.2); // Not true, it's something in the middle. + + var midX = (xRange[1] - xRange[0]) / 2; + DygraphOps.dispatchMouseDown(g, midX, yRange[0]); + DygraphOps.dispatchMouseMove(g, midX, yRange[1]); // this is really necessary. + DygraphOps.dispatchMouseUp(g, midX, yRange[1]); + + assertEqualsDelta(xRange, g.xAxisRange(), 0.2); + assertEqualsDelta(yRange, g.yAxisRange(0), 0.2); +} + +/** + * This tests that when changing the interaction model so pan is used instead + * of zoom as the default behavior, a standard click method is still called. + */ +InteractionModelTestCase.prototype.testClickCallbackIsCalled = function() { + var clicked; + + var clickCallback = function(event, x) { + clicked = x; + }; + + var data = "X,Y\n" + + "20,-1\n" + + "21,0\n" + + "22,1\n" + + "23,0\n" + ; + + var graph = document.getElementById("graph"); + var g = new Dygraph(graph, data, + { + width: 100, + height : 100, + clickCallback : clickCallback + }); + + DygraphOps.dispatchMouseDown_Point(g, 10, 10); + DygraphOps.dispatchMouseMove_Point(g, 10, 10); + DygraphOps.dispatchMouseUp_Point(g, 10, 10); + + assertEquals(20, clicked); +}; + +/** + * This tests that when changing the interaction model so pan is used instead + * of zoom as the default behavior, a standard click method is still called. + */ +InteractionModelTestCase.prototype.testClickCallbackIsCalledOnCustomPan = function() { + var clicked; + + var clickCallback = function(event, x) { + clicked = x; + }; + + var data = "X,Y\n" + + "20,-1\n" + + "21,0\n" + + "22,1\n" + + "23,0\n" + ; + + function customDown(event, g, context) { + context.initializeMouseDown(event, g, context); + Dygraph.startPan(event, g, context); + } + + function customMove(event, g, context) { + Dygraph.movePan(event, g, context); + } + + function customUp(event, g, context) { + Dygraph.endPan(event, g, context); + } + + var opts = { + width: 100, + height : 100, + clickCallback : clickCallback, + interactionModel : { + 'mousedown' : customDown, + 'mousemove' : customMove, + 'mouseup' : customUp, + } + }; + + var graph = document.getElementById("graph"); + var g = new Dygraph(graph, data, opts); + + DygraphOps.dispatchMouseDown_Point(g, 10, 10); + DygraphOps.dispatchMouseMove_Point(g, 10, 10); + DygraphOps.dispatchMouseUp_Point(g, 10, 10); + + assertEquals(20, clicked); +}; + diff --git a/auto_tests/tests/range_tests.js b/auto_tests/tests/range_tests.js index 561ceeb..036fc1b 100644 --- a/auto_tests/tests/range_tests.js +++ b/auto_tests/tests/range_tests.js @@ -84,7 +84,6 @@ RangeTestCase.prototype.zoom = function(g, xRange, yRange) { var originalXRange = g.xAxisRange(); var originalYRange = g.yAxisRange(0); - // Editing e.shiftKey post construction doesn't work for Firefox. Damn. DygraphOps.dispatchMouseDown(g, xRange[0], yRange[0]); DygraphOps.dispatchMouseMove(g, xRange[1], yRange[0]); // this is really necessary. DygraphOps.dispatchMouseUp(g, xRange[1], yRange[0]); diff --git a/dygraph.js b/dygraph.js index 2e8a38e..61e8e49 100644 --- a/dygraph.js +++ b/dygraph.js @@ -1254,6 +1254,16 @@ Dygraph.Interaction.endPan = function(event, g, context) { context.valueRange = null; context.boundedDates = null; context.boundedValues = null; + + var dragEndX = g.dragGetX_(event, context); + var dragEndY = g.dragGetY_(event, context); + var regionWidth = Math.abs(context.dragEndX - context.dragStartX); + var regionHeight = Math.abs(context.dragEndY - context.dragStartY); + + if (regionWidth < 2 && regionHeight < 2 && + g.lastx_ != undefined && g.lastx_ != -1) { + Dygraph.Interaction.treatMouseOpAsClick(g); + } }; /** @@ -1311,6 +1321,33 @@ Dygraph.Interaction.moveZoom = function(event, g, context) { context.prevDragDirection = context.dragDirection; }; +Dygraph.Interaction.treatMouseOpAsClick = function(g) { + // TODO(danvk): pass along more info about the points, e.g. 'x' + if (g.attr_('clickCallback') != null) { + g.attr_('clickCallback')(event, g.lastx_, g.selPoints_); + } + if (g.attr_('pointClickCallback')) { + // check if the click was on a particular point. + var closestIdx = -1; + var closestDistance = 0; + for (var i = 0; i < g.selPoints_.length; i++) { + var p = g.selPoints_[i]; + var distance = Math.pow(p.canvasx - context.dragEndX, 2) + + Math.pow(p.canvasy - context.dragEndY, 2); + if (closestIdx == -1 || distance < closestDistance) { + closestDistance = distance; + closestIdx = i; + } + } + + // Allow any click within two pixels of the dot. + var radius = g.attr_('highlightCircleSize') + 2; + if (closestDistance <= 5 * 5) { + g.attr_('pointClickCallback')(event, g.selPoints_[closestIdx]); + } + } +} + /** * Called in response to an interaction model operation that * responds to an event that performs a zoom based on previously defined @@ -1326,7 +1363,6 @@ Dygraph.Interaction.moveZoom = function(event, g, context) { * dragStartX/dragStartY/etc. properties). This function modifies the context. */ Dygraph.Interaction.endZoom = function(event, g, context) { - // TODO(konigsberg): Refactor or rename this fn -- it deals with clicks, too. context.isZooming = false; context.dragEndX = g.dragGetX_(event, context); context.dragEndY = g.dragGetY_(event, context); @@ -1335,30 +1371,7 @@ Dygraph.Interaction.endZoom = function(event, g, context) { if (regionWidth < 2 && regionHeight < 2 && g.lastx_ != undefined && g.lastx_ != -1) { - // TODO(danvk): pass along more info about the points, e.g. 'x' - if (g.attr_('clickCallback') != null) { - g.attr_('clickCallback')(event, g.lastx_, g.selPoints_); - } - if (g.attr_('pointClickCallback')) { - // check if the click was on a particular point. - var closestIdx = -1; - var closestDistance = 0; - for (var i = 0; i < g.selPoints_.length; i++) { - var p = g.selPoints_[i]; - var distance = Math.pow(p.canvasx - context.dragEndX, 2) + - Math.pow(p.canvasy - context.dragEndY, 2); - if (closestIdx == -1 || distance < closestDistance) { - closestDistance = distance; - closestIdx = i; - } - } - - // Allow any click within two pixels of the dot. - var radius = g.attr_('highlightCircleSize') + 2; - if (closestDistance <= 5 * 5) { - g.attr_('pointClickCallback')(event, g.selPoints_[closestIdx]); - } - } + Dygraph.Interaction.treatMouseOpAsClick(g); } if (regionWidth >= 10 && context.dragDirection == Dygraph.HORIZONTAL) { -- 2.7.4