Skip to content

Fix issue 359 (zoom overlay drawn beneath shapes) #448

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Apr 27, 2016
1 change: 1 addition & 0 deletions src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2654,6 +2654,7 @@ function makePlotFramework(gd) {
// these are in a different svg element normally, but get collapsed into a single
// svg when exporting (after inserting 3D)
fullLayout._infolayer = fullLayout._toppaper.append('g').classed('infolayer', true);
fullLayout._zoomlayer = fullLayout._toppaper.append('g').classed('zoomlayer', true);
fullLayout._hoverlayer = fullLayout._toppaper.append('g').classed('hoverlayer', true);

gd.emit('plotly_framework');
Expand Down
13 changes: 9 additions & 4 deletions src/plots/cartesian/dragbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {

dragElement.init(dragOptions);

var x0,
var zoomlayer = gd._fullLayout._zoomlayer,
xs = plotinfo.x()._offset,
ys = plotinfo.y()._offset,
x0,
y0,
box,
lum,
Expand All @@ -161,22 +164,24 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
dimmed = false;
zoomMode = 'xy';

zb = plotinfo.plot.append('path')
zb = zoomlayer.append('path')
.attr('class', 'zoombox')
.style({
'fill': lum>0.2 ? 'rgba(0,0,0,0)' : 'rgba(255,255,255,0)',
'stroke-width': 0
})
.attr('transform','translate(' + xs + ', ' + ys + ')')
.attr('d', path0 + 'Z');

corners = plotinfo.plot.append('path')
corners = zoomlayer.append('path')
.attr('class', 'zoombox-corners')
.style({
fill: Color.background,
stroke: Color.defaultLine,
'stroke-width': 1,
opacity: 0
})
.attr('transform','translate(' + xs + ', ' + ys + ')')
.attr('d','M0,0Z');

clearSelect();
Expand All @@ -187,7 +192,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
// until we get around to persistent selections, remove the outline
// here. The selection itself will be removed when the plot redraws
// at the end.
plotinfo.plot.selectAll('.select-outline').remove();
zoomlayer.selectAll('.select-outline').remove();
}

function zoomMove(dx0, dy0) {
Expand Down
8 changes: 6 additions & 2 deletions src/plots/cartesian/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ var MINSELECT = constants.MINSELECT;
function getAxId(ax) { return ax._id; }

module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
var plot = dragOptions.plotinfo.plot,
var plot = dragOptions.gd._fullLayout._zoomlayer,
dragBBox = dragOptions.element.getBoundingClientRect(),
xs = dragOptions.plotinfo.x()._offset,
ys = dragOptions.plotinfo.y()._offset,
x0 = startX - dragBBox.left,
y0 = startY - dragBBox.top,
x1 = x0,
Expand All @@ -45,6 +47,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
outlines.enter()
.append('path')
.attr('class', function(d) { return 'select-outline select-outline-' + d; })
.attr('transform','translate(' + xs + ', ' + ys + ')')
.attr('d', path0 + 'Z');

var corners = plot.append('path')
Expand All @@ -54,6 +57,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
stroke: color.defaultLine,
'stroke-width': 1
})
.attr('transform','translate(' + xs + ', ' + ys + ')')
.attr('d','M0,0Z');


Expand Down Expand Up @@ -176,6 +180,7 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
};

dragOptions.doneFn = function(dragged, numclicks) {
corners.remove();
if(!dragged && numclicks === 2) {
// clear selection on doubleclick
outlines.remove();
Expand All @@ -189,6 +194,5 @@ module.exports = function prepSelect(e, startX, startY, dragOptions, mode) {
else {
dragOptions.gd.emit('plotly_selected', eventData);
}
corners.remove();
};
};
51 changes: 51 additions & 0 deletions test/jasmine/tests/cartesian_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
var d3 = require('d3');

var Plotly = require('@lib/index');
var Lib = require('@src/lib');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var mouseEvent = require('../assets/mouse_event');


describe('zoom box element', function() {
var mock = require('@mocks/14.json');

var gd;
beforeEach(function(done) {
gd = createGraphDiv();

var mockCopy = Lib.extendDeep({}, mock);
mockCopy.layout.dragmode = 'zoom';

Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
});

afterEach(destroyGraphDiv);

it('should be appended to the zoom layer', function() {
var x0 = 100;
var y0 = 200;
var x1 = 150;
var y1 = 200;

mouseEvent('mousemove', x0, y0);
expect(d3.selectAll('.zoomlayer > .zoombox').size())
.toEqual(0);
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
.toEqual(0);

mouseEvent('mousedown', x0, y0);
mouseEvent('mousemove', x1, y1);
expect(d3.selectAll('.zoomlayer > .zoombox').size())
.toEqual(1);
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
.toEqual(1);

mouseEvent('mouseup', x1, y1);
expect(d3.selectAll('.zoomlayer > .zoombox').size())
.toEqual(0);
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
.toEqual(0);
});
});
8 changes: 7 additions & 1 deletion test/jasmine/tests/click_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,13 @@ describe('Test click interactions:', function() {

describe('drag interactions', function() {
beforeEach(function(done) {
Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(done);
Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() {
// Do not let the notifier hide the drag elements
var tooltip = document.querySelector('.notifier-note');
if(tooltip) tooltip.style.display = 'None';

done();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beautiful. Thanks for figuring this out 🍻

});
});

it('on nw dragbox should update the axis ranges', function(done) {
Expand Down
100 changes: 100 additions & 0 deletions test/jasmine/tests/select_test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
var d3 = require('d3');

var Plotly = require('@lib/index');
var Lib = require('@src/lib');
var DBLCLICKDELAY = require('@src/plots/cartesian/constants').DBLCLICKDELAY;
Expand Down Expand Up @@ -54,6 +56,104 @@ describe('select box and lasso', function() {
expect(actual.y).toBeCloseToArray(expected.y, PRECISION);
}

describe('select elements', function() {
var mockCopy = Lib.extendDeep({}, mock);
mockCopy.layout.dragmode = 'select';

var gd;
beforeEach(function(done) {
gd = createGraphDiv();

Plotly.plot(gd, mockCopy.data, mockCopy.layout)
.then(done);
});

it('should be appended to the zoom layer', function(done) {
var x0 = 100,
y0 = 200,
x1 = 150,
y1 = 250,
x2 = 50,
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);
});

gd.once('plotly_selected', function() {
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
.toEqual(0);
expect(d3.selectAll('.zoomlayer > .select-outline').size())
.toEqual(2);
});

gd.once('plotly_deselect', function() {
expect(d3.selectAll('.zoomlayer > .select-outline').size())
.toEqual(0);
});

mouseEvent('mousemove', x0, y0);
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
.toEqual(0);

drag([[x0, y0], [x1, y1]]);

doubleClick(x2, y2, done);
});
});

describe('lasso elements', function() {
var mockCopy = Lib.extendDeep({}, mock);
mockCopy.layout.dragmode = 'lasso';

var gd;
beforeEach(function(done) {
gd = createGraphDiv();

Plotly.plot(gd, mockCopy.data, mockCopy.layout)
.then(done);
});

it('should be appended to the zoom layer', function(done) {
var x0 = 100,
y0 = 200,
x1 = 150,
y1 = 250,
x2 = 50,
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);
});

gd.once('plotly_selected', function() {
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
.toEqual(0);
expect(d3.selectAll('.zoomlayer > .select-outline').size())
.toEqual(2);
});

gd.once('plotly_deselect', function() {
expect(d3.selectAll('.zoomlayer > .select-outline').size())
.toEqual(0);
});

mouseEvent('mousemove', x0, y0);
expect(d3.selectAll('.zoomlayer > .zoombox-corners').size())
.toEqual(0);

drag([[x0, y0], [x1, y1]]);

doubleClick(x2, y2, done);
});
});

describe('select events', function() {
var mockCopy = Lib.extendDeep({}, mock);
mockCopy.layout.dragmode = 'select';
Expand Down