diff --git a/src/plots/cartesian/select.js b/src/plots/cartesian/select.js index cba5decac75..045585cdbac 100644 --- a/src/plots/cartesian/select.js +++ b/src/plots/cartesian/select.js @@ -23,7 +23,7 @@ var MINSELECT = constants.MINSELECT; function getAxId(ax) { return ax._id; } module.exports = function prepSelect(e, startX, startY, dragOptions, mode) { - var plot = dragOptions.gd._fullLayout._zoomlayer, + var zoomLayer = dragOptions.gd._fullLayout._zoomlayer, dragBBox = dragOptions.element.getBoundingClientRect(), xs = dragOptions.plotinfo.xaxis._offset, ys = dragOptions.plotinfo.yaxis._offset, @@ -43,7 +43,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) { pts = filteredPolygon([[x0, y0]], constants.BENDPX); } - var outlines = plot.selectAll('path.select-outline').data([1, 2]); + var outlines = zoomLayer.selectAll('path.select-outline').data([1, 2]); outlines.enter() .append('path') @@ -51,7 +51,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) { .attr('transform', 'translate(' + xs + ', ' + ys + ')') .attr('d', path0 + 'Z'); - var corners = plot.append('path') + var corners = zoomLayer.append('path') .attr('class', 'zoombox-corners') .style({ fill: color.background, diff --git a/src/plots/ternary/index.js b/src/plots/ternary/index.js index e1da80af7b1..2b24ed0938a 100644 --- a/src/plots/ternary/index.js +++ b/src/plots/ternary/index.js @@ -69,4 +69,8 @@ exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) oldTernary.clipDef.remove(); } } + + if(oldFullLayout._zoomlayer) { + oldFullLayout._zoomlayer.selectAll('.select-outline').remove(); + } }; diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index fa9fcbfcf67..4670167ca18 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -88,7 +88,6 @@ proto.makeFramework = function() { 'backplot', 'grids', 'frontplot', - 'zoom', 'aaxis', 'baxis', 'caxis', 'axlines' ]; var toplevel = _this.plotContainer.selectAll('g.toplevel') @@ -123,10 +122,6 @@ proto.makeFramework = function() { _this.plotContainer.selectAll('.backplot,.frontplot,.grids') .call(Drawing.setClipUrl, clipId); - - if(!_this.graphDiv._context.staticPlot) { - _this.initInteractions(); - } }; var w_over_h = Math.sqrt(4 / 3); @@ -263,7 +258,7 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { _this.layers.plotbg.select('path').attr('d', triangleClip); var plotTransform = 'translate(' + x0 + ',' + y0 + ')'; - _this.plotContainer.selectAll('.scatterlayer,.maplayer,.zoom') + _this.plotContainer.selectAll('.scatterlayer,.maplayer') .attr('transform', plotTransform); // TODO: shift axes to accommodate linewidth*sin(30) tick mark angle @@ -303,6 +298,10 @@ proto.adjustLayout = function(ternaryLayout, graphSize) { 'M' + (x0 + w / 2) + ',' + y0 + 'l' + (w / 2) + ',' + h : 'M0,0') .call(Color.stroke, caxis.linecolor || '#000') .style('stroke-width', (caxis.linewidth || 0) + 'px'); + + if(!_this.graphDiv._context.staticPlot) { + _this.initInteractions(); + } }; proto.drawAxes = function(doTitles) { @@ -382,13 +381,16 @@ proto.initInteractions = function() { var _this = this, dragger = _this.layers.plotbg.select('path').node(), gd = _this.graphDiv, - zoomContainer = _this.layers.zoom; + zoomContainer = gd._fullLayout._zoomlayer; // use plotbg for the main interactions var dragOptions = { element: dragger, gd: gd, - plotinfo: {plot: zoomContainer}, + plotinfo: { + xaxis: _this.xaxis, + yaxis: _this.yaxis + }, doubleclick: doubleClick, subplot: _this.id, prepFn: function(e, startX, startY) { @@ -441,6 +443,7 @@ proto.initInteractions = function() { zb = zoomContainer.append('path') .attr('class', 'zoombox') + .attr('transform', 'translate(' + _this.x0 + ', ' + _this.y0 + ')') .style({ 'fill': lum > 0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)', 'stroke-width': 0 @@ -449,6 +452,7 @@ proto.initInteractions = function() { corners = zoomContainer.append('path') .attr('class', 'zoombox-corners') + .attr('transform', 'translate(' + _this.x0 + ', ' + _this.y0 + ')') .style({ fill: Color.background, stroke: Color.defaultLine, @@ -603,7 +607,7 @@ proto.initInteractions = function() { // until we get around to persistent selections, remove the outline // here. The selection itself will be removed when the plot redraws // at the end. - _this.plotContainer.selectAll('.select-outline').remove(); + zoomContainer.selectAll('.select-outline').remove(); } function doubleClick() { diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 37cf778b940..86d0825700c 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -6,6 +6,7 @@ var doubleClick = require('../assets/double_click'); var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); +var fail = require('../assets/fail_test'); var mouseEvent = require('../assets/mouse_event'); var customMatchers = require('../assets/custom_matchers'); @@ -16,7 +17,7 @@ describe('select box and lasso', function() { var selectPath = [[93, 193], [143, 193]]; var lassoPath = [[316, 171], [318, 239], [335, 243], [328, 169]]; - beforeEach(function() { + beforeAll(function() { jasmine.addMatchers(customMatchers); }); @@ -59,6 +60,13 @@ describe('select box and lasso', function() { }); } + function assertSelectionNodes(cornerCnt, outlineCnt) { + expect(d3.selectAll('.zoomlayer > .zoombox-corners').size()) + .toBe(cornerCnt, 'selection corner count'); + expect(d3.selectAll('.zoomlayer > .select-outline').size()) + .toBe(outlineCnt, 'selection outline count'); + } + describe('select elements', function() { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.dragmode = 'select'; @@ -80,30 +88,21 @@ describe('select box and lasso', function() { y2 = 50; gd.once('plotly_selecting', function() { - expect(d3.selectAll('.zoomlayer > .zoombox-corners').size()) - .toEqual(1); - expect(d3.selectAll('.zoomlayer > .select-outline').size()) - .toEqual(2); + assertSelectionNodes(1, 2); }); gd.once('plotly_selected', function() { - expect(d3.selectAll('.zoomlayer > .zoombox-corners').size()) - .toEqual(0); - expect(d3.selectAll('.zoomlayer > .select-outline').size()) - .toEqual(2); + assertSelectionNodes(0, 2); }); gd.once('plotly_deselect', function() { - expect(d3.selectAll('.zoomlayer > .select-outline').size()) - .toEqual(0); + assertSelectionNodes(0, 0); }); mouseEvent('mousemove', x0, y0); - expect(d3.selectAll('.zoomlayer > .zoombox-corners').size()) - .toEqual(0); + assertSelectionNodes(0, 0); drag([[x0, y0], [x1, y1]]); - doubleClick(x2, y2).then(done); }); }); @@ -129,30 +128,21 @@ describe('select box and lasso', function() { y2 = 50; gd.once('plotly_selecting', function() { - expect(d3.selectAll('.zoomlayer > .zoombox-corners').size()) - .toEqual(1); - expect(d3.selectAll('.zoomlayer > .select-outline').size()) - .toEqual(2); + assertSelectionNodes(1, 2); }); gd.once('plotly_selected', function() { - expect(d3.selectAll('.zoomlayer > .zoombox-corners').size()) - .toEqual(0); - expect(d3.selectAll('.zoomlayer > .select-outline').size()) - .toEqual(2); + assertSelectionNodes(0, 2); }); gd.once('plotly_deselect', function() { - expect(d3.selectAll('.zoomlayer > .select-outline').size()) - .toEqual(0); + assertSelectionNodes(0, 0); }); mouseEvent('mousemove', x0, y0); - expect(d3.selectAll('.zoomlayer > .zoombox-corners').size()) - .toEqual(0); + assertSelectionNodes(0, 0); drag([[x0, y0], [x1, y1]]); - doubleClick(x2, y2).then(done); }); }); @@ -379,4 +369,95 @@ describe('select box and lasso', function() { }) .then(done); }); + + it('should work on scatterternary traces', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/ternary_simple')); + var gd = createGraphDiv(); + var pts = []; + + fig.layout.width = 800; + fig.layout.dragmode = 'select'; + + function assertPoints(expected) { + expect(pts.length).toBe(expected.length, 'selected points length'); + + pts.forEach(function(p, i) { + var e = expected[i]; + expect(p.a).toBe(e.a, 'selected pt a val'); + expect(p.b).toBe(e.b, 'selected pt b val'); + expect(p.c).toBe(e.c, 'selected pt c val'); + }); + pts = []; + } + + Plotly.plot(gd, fig).then(function() { + gd.on('plotly_selected', function(data) { + pts = data.points; + }); + + assertSelectionNodes(0, 0); + drag([[400, 200], [445, 235]]); + assertSelectionNodes(0, 2); + assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]); + + return Plotly.relayout(gd, 'dragmode', 'lasso'); + }) + .then(function() { + assertSelectionNodes(0, 0); + drag([[400, 200], [445, 200], [445, 235], [400, 235], [400, 200]]); + assertSelectionNodes(0, 2); + assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]); + + // should work after a relayout too + return Plotly.relayout(gd, 'width', 400); + }) + .then(function() { + assertSelectionNodes(0, 0); + drag([[200, 200], [230, 200], [230, 230], [200, 230], [200, 200]]); + assertSelectionNodes(0, 2); + assertPoints([{ a: 0.5, b: 0.25, c: 0.25 }]); + }) + .catch(fail) + .then(done); + }); + + it('should work on scattercarpet traces', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/scattercarpet')); + var gd = createGraphDiv(); + var pts = []; + + fig.layout.dragmode = 'select'; + + function assertPoints(expected) { + expect(pts.length).toBe(expected.length, 'selected points length'); + + pts.forEach(function(p, i) { + var e = expected[i]; + expect(p.a).toBe(e.a, 'selected pt a val'); + expect(p.b).toBe(e.b, 'selected pt b val'); + }); + pts = []; + } + + Plotly.plot(gd, fig).then(function() { + gd.on('plotly_selected', function(data) { + pts = data.points; + }); + + assertSelectionNodes(0, 0); + drag([[300, 200], [400, 250]]); + assertSelectionNodes(0, 2); + assertPoints([{ a: 0.2, b: 1.5 }]); + + return Plotly.relayout(gd, 'dragmode', 'lasso'); + }) + .then(function() { + assertSelectionNodes(0, 0); + drag([[300, 200], [400, 200], [400, 250], [300, 250], [300, 200]]); + assertSelectionNodes(0, 2); + assertPoints([{ a: 0.2, b: 1.5 }]); + }) + .catch(fail) + .then(done); + }); });