diff --git a/src/components/fx/layout_attributes.js b/src/components/fx/layout_attributes.js index 542be419e6d..4c35c2b4086 100644 --- a/src/components/fx/layout_attributes.js +++ b/src/components/fx/layout_attributes.js @@ -101,5 +101,17 @@ module.exports = { ].join(' ') }, editType: 'none' + }, + selectdirection: { + valType: 'enumerated', + role: 'info', + values: ['h', 'v', 'd', 'any'], + dflt: 'any', + description: [ + 'When "dragmode" is set to "select", this limits the selection of the drag to', + 'horizontal, vertical or diagonal. "h" only allows horizontal selection,', + '"v" only vertical, "d" only diagonal and "any" sets no limit.' + ].join(' '), + editType: 'none' } }; diff --git a/src/components/fx/layout_defaults.js b/src/components/fx/layout_defaults.js index b88b652d9e2..742c6eb1621 100644 --- a/src/components/fx/layout_defaults.js +++ b/src/components/fx/layout_defaults.js @@ -16,7 +16,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt); } - coerce('dragmode'); + var dragMode = coerce('dragmode'); + if(dragMode === 'select') coerce('selectdirection'); var hovermodeDflt; if(layoutOut._has('cartesian')) { diff --git a/src/plots/cartesian/select.js b/src/plots/cartesian/select.js index 4eda20dec49..5dd418441f8 100644 --- a/src/plots/cartesian/select.js +++ b/src/plots/cartesian/select.js @@ -200,7 +200,18 @@ function prepSelect(e, startX, startY, dragOptions, mode) { dy = Math.abs(y1 - y0); if(mode === 'select') { - if(dy < Math.min(dx * 0.6, MINSELECT)) { + var direction = fullLayout.selectdirection; + + if(fullLayout.selectdirection === 'any') { + if(dy < Math.min(dx * 0.6, MINSELECT)) direction = 'h'; + else if(dx < Math.min(dy * 0.6, MINSELECT)) direction = 'v'; + else direction = 'd'; + } + else { + direction = fullLayout.selectdirection; + } + + if(direction === 'h') { // horizontal motion: make a vertical box currentPolygon = [[x0, 0], [x0, ph], [x1, ph], [x1, 0]]; currentPolygon.xmin = Math.min(x0, x1); @@ -214,7 +225,7 @@ function prepSelect(e, startX, startY, dragOptions, mode) { 'h4v' + (2 * MINSELECT) + 'h-4Z'); } - else if(dx < Math.min(dy * 0.6, MINSELECT)) { + else if(direction === 'v') { // vertical motion: make a horizontal box currentPolygon = [[0, y0], [0, y1], [pw, y1], [pw, y0]]; currentPolygon.xmin = Math.min(0, pw); @@ -226,7 +237,7 @@ function prepSelect(e, startX, startY, dragOptions, mode) { 'M' + (x0 - MINSELECT) + ',' + (currentPolygon.ymax - 1) + 'v4h' + (2 * MINSELECT) + 'v-4Z'); } - else { + else if(direction === 'd') { // diagonal motion currentPolygon = [[x0, y0], [x0, y1], [x1, y1], [x1, y0]]; currentPolygon.xmin = Math.min(x0, x1); diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 992a54b5f3a..81931219d6d 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -552,6 +552,69 @@ describe('@flaky Test select box and lasso in general:', function() { .catch(failTest) .then(done); }); + + it('should select the right data with the corresponding select direction', function(done) { + + var gd = createGraphDiv(); + + // drag around just the center point, but if we have a selectdirection we may + // get either the ones to the left and right or above and below + var selectPath = [[175, 175], [225, 225]]; + + function selectDrag() { + resetEvents(gd); + drag(selectPath); + return selectedPromise; + } + + function assertSelectedPointNumbers(pointNumbers) { + var pts = selectedData.points; + expect(pts.length).toBe(pointNumbers.length); + pointNumbers.forEach(function(pointNumber, i) { + expect(pts[i].pointNumber).toBe(pointNumber); + }); + } + + Plotly.newPlot(gd, [{ + x: [1, 1, 1, 2, 2, 2, 3, 3, 3], + y: [1, 2, 3, 1, 2, 3, 1, 2, 3], + mode: 'markers' + }], { + width: 400, + height: 400, + dragmode: 'select', + margin: {l: 100, r: 100, t: 100, b: 100}, + xaxis: {range: [0, 4]}, + yaxis: {range: [0, 4]} + }) + .then(selectDrag) + .then(function() { + expect(gd._fullLayout.selectdirection).toBe('any'); + assertSelectedPointNumbers([4]); + + return Plotly.relayout(gd, {selectdirection: 'h'}); + }) + .then(selectDrag) + .then(function() { + assertSelectedPointNumbers([3, 4, 5]); + + return Plotly.relayout(gd, {selectdirection: 'v'}); + }) + .then(selectDrag) + .then(function() { + assertSelectedPointNumbers([1, 4, 7]); + + return Plotly.relayout(gd, {selectdirection: 'd'}); + }) + .then(selectDrag) + .then(function() { + assertSelectedPointNumbers([4]); + }) + .catch(failTest) + .then(done); + + }); + }); describe('@flaky Test select box and lasso per trace:', function() { diff --git a/test/jasmine/tests/shapes_test.js b/test/jasmine/tests/shapes_test.js index c5c9b5e77b6..7463db6f01d 100644 --- a/test/jasmine/tests/shapes_test.js +++ b/test/jasmine/tests/shapes_test.js @@ -1026,7 +1026,7 @@ describe('A fixed size shape', function() { var shapeAndResizeTypes = combinations(shapeTypes, resizeTypes); shapeAndResizeTypes.forEach(function(testCase) { - describe('of type ' + testCase.type + ' can be ' + testCase.resizeDisplayName, function() { + describe('@flaky of type ' + testCase.type + ' can be ' + testCase.resizeDisplayName, function() { resizeDirections.forEach(function(direction) { it('over direction ' + direction, function(done) { layout.shapes[0].type = testCase.type;