Skip to content

Commit c7a7f59

Browse files
committed
add polar drag interaction tests
++ cleanup some view intial logic ++ rename inner <g.polarlayer> -> <g.polarsublayer> to not conflict with <g.polarlayer> off fullLayout._paper
1 parent 8a33b89 commit c7a7f59

File tree

3 files changed

+257
-19
lines changed

3 files changed

+257
-19
lines changed

src/plots/polar/polar.js

+13-12
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ function Polar(gd, id) {
4545
this.layers = {};
4646
this.clipPaths = {};
4747
this.clipIds = {};
48+
this.viewInitial = {};
4849

4950
var fullLayout = gd._fullLayout;
5051
var clipIdBase = 'clip' + fullLayout._uid + id;
@@ -57,9 +58,6 @@ function Polar(gd, id) {
5758
this.framework = fullLayout._polarlayer.append('g')
5859
.attr('class', id);
5960

60-
// TODO should radialaxis angle be part of view initial?
61-
this.viewInitial = {};
62-
6361
// unfortunately, we have to keep track of some axis tick settings
6462
// so that we don't have to call Axes.doTicks with its special redraw flag
6563
this.radialTickLayout = null;
@@ -117,11 +115,11 @@ proto.updateLayers = function(fullLayout, polarLayout) {
117115
if(!isAngularAxisBelowTraces) layerData.push('angular-line');
118116
if(!isRadialAxisBelowTraces) layerData.push('radial-line');
119117

120-
var join = _this.framework.selectAll('.polarlayer')
118+
var join = _this.framework.selectAll('.polarsublayer')
121119
.data(layerData, String);
122120

123121
join.enter().append('g')
124-
.attr('class', function(d) { return 'polarlayer ' + d;})
122+
.attr('class', function(d) { return 'polarsublayer ' + d;})
125123
.each(function(d) {
126124
var sel = layers[d] = d3.select(this);
127125

@@ -269,6 +267,8 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) {
269267
var sector = polarLayout.sector;
270268
var a0 = wrap360(sector[0]);
271269

270+
_this.fillViewInitialKey('radialaxis.angle', radialLayout.angle);
271+
272272
var ax = _this.radialAxis = Lib.extendFlat({}, radialLayout, {
273273
_axislayer: layers['radial-axis'],
274274
_gridlayer: layers['radial-grid'],
@@ -295,10 +295,7 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) {
295295
Axes.doAutoRange(ax);
296296
radialLayout.range = ax.range.slice();
297297
radialLayout._input.range = ax.range.slice();
298-
299-
if(!('radialaxis.range' in _this.viewInitial)) {
300-
_this.viewInitial['radialaxis.range'] = ax.range.slice();
301-
}
298+
_this.fillViewInitialKey('radialaxis.range', ax.range.slice());
302299

303300
// rotate auto tick labels by 180 if in quadrant II and III to make them
304301
// readable from left-to-right
@@ -396,9 +393,7 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) {
396393
var sector = polarLayout.sector;
397394
var sectorInRad = sector.map(deg2rad);
398395

399-
if(!('angularaxis.rotation' in _this.viewInitial)) {
400-
_this.viewInitial['angularaxis.rotation'] = angularLayout.rotation;
401-
}
396+
_this.fillViewInitialKey('angularaxis.rotation', angularLayout.rotation);
402397

403398
var ax = _this.angularAxis = Lib.extendFlat({}, angularLayout, {
404399
_axislayer: layers['angular-axis'],
@@ -1018,6 +1013,12 @@ proto.isPtWithinSector = function(d) {
10181013
);
10191014
};
10201015

1016+
proto.fillViewInitialKey = function(key, val) {
1017+
if(!(key in this.viewInitial)) {
1018+
this.viewInitial[key] = val;
1019+
}
1020+
};
1021+
10211022
function setScale(ax, axLayout, fullLayout) {
10221023
Axes.setConvert(ax, fullLayout);
10231024

test/jasmine/assets/drag.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
var isNumeric = require('fast-isnumeric');
12
var mouseEvent = require('./mouse_event');
23
var getNodeCoords = require('./get_node_coords');
34

@@ -6,12 +7,10 @@ var getNodeCoords = require('./get_node_coords');
67
* optionally specify an edge ('n', 'se', 'w' etc)
78
* to grab it by an edge or corner (otherwise the middle is used)
89
*/
9-
module.exports = function(node, dx, dy, edge) {
10-
10+
module.exports = function(node, dx, dy, edge, x0, y0) {
1111
var coords = getNodeCoords(node, edge);
12-
var fromX = coords.x;
13-
var fromY = coords.y;
14-
12+
var fromX = isNumeric(x0) ? x0 : coords.x;
13+
var fromY = isNumeric(y0) ? y0 : coords.y;
1514
var toX = fromX + dx;
1615
var toY = fromY + dy;
1716

test/jasmine/tests/polar_test.js

+240-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ var fail = require('../assets/fail_test');
1010
var mouseEvent = require('../assets/mouse_event');
1111
var click = require('../assets/click');
1212
var doubleClick = require('../assets/double_click');
13+
var drag = require('../assets/drag');
14+
var delay = require('../assets/delay');
1315

1416
describe('Test legacy polar plots logs:', function() {
1517
var gd;
@@ -177,14 +179,14 @@ describe('Test relayout on polar subplots:', function() {
177179
var dflt = constants.layerNames;
178180

179181
function _assert(expected) {
180-
var actual = d3.selectAll('g.polar > .polarlayer');
182+
var actual = d3.selectAll('g.polar > .polarsublayer');
181183

182184
expect(actual.size()).toBe(expected.length, '# of layer');
183185

184186
actual.each(function(d, i) {
185187
var className = d3.select(this)
186188
.attr('class')
187-
.split('polarlayer ')[1];
189+
.split('polarsublayer ')[1];
188190

189191
expect(className).toBe(expected[i], 'layer ' + i);
190192
});
@@ -698,4 +700,240 @@ describe('Test polar interactions:', function() {
698700
.catch(fail)
699701
.then(done);
700702
});
703+
704+
it('should response to drag interactions on plot area', function(done) {
705+
var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json'));
706+
707+
// adjust margins so that middle of plot area is at 300x300
708+
// with its middle at [200,200]
709+
fig.layout.width = 400;
710+
fig.layout.height = 400;
711+
fig.layout.margin = {l: 50, t: 50, b: 50, r: 50};
712+
713+
var mid = [200, 200];
714+
var relayoutNumber = 0;
715+
var resetNumber = 0;
716+
717+
function _drag(p0, dp) {
718+
var node = d3.select('.polar > .draglayer > .maindrag').node();
719+
return drag(node, dp[0], dp[1], null, p0[0], p0[1]);
720+
}
721+
722+
function _assertRange(rng, msg) {
723+
expect(gd._fullLayout.polar.radialaxis.range).toBeCloseToArray(rng, 1, msg);
724+
}
725+
726+
function _assertDrag(rng, msg) {
727+
relayoutNumber++;
728+
_assertRange(rng, msg);
729+
730+
if(eventCnts.plotly_relayout === relayoutNumber) {
731+
expect(eventData['polar.radialaxis.range'])
732+
.toBeCloseToArray(rng, 1, msg + '- event data');
733+
} else {
734+
fail('incorrect number of plotly_relayout events triggered - ' + msg);
735+
}
736+
}
737+
738+
function _assertBase(extra) {
739+
var msg = 'base range' + (extra ? ' ' + extra : '');
740+
_assertRange([0, 11.1], msg);
741+
}
742+
743+
function _reset() {
744+
return _doubleClick(mid).then(function() {
745+
relayoutNumber++;
746+
resetNumber++;
747+
748+
var extra = '(reset ' + resetNumber + ')';
749+
_assertBase(extra);
750+
expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
751+
});
752+
}
753+
754+
_plot(fig)
755+
.then(_assertBase)
756+
.then(function() { return _drag(mid, [50, 50]); })
757+
.then(function() {
758+
_assertDrag([0, 5.24], 'from center move toward bottom-right');
759+
})
760+
.then(_reset)
761+
.then(function() { return _drag(mid, [-50, -50]); })
762+
.then(function() {
763+
_assertDrag([0, 5.24], 'from center move toward top-left');
764+
})
765+
.then(_reset)
766+
.then(function() { return _drag([mid[0] + 30, mid[0] - 30], [50, -50]); })
767+
.then(function() {
768+
_assertDrag([3.1, 8.4], 'from quadrant #1 move top-right');
769+
})
770+
.then(_reset)
771+
.then(function() { return _drag([345, 200], [-50, 0]); })
772+
.then(function() {
773+
_assertDrag([7.0, 11.1], 'from right edge move left');
774+
})
775+
.then(_reset)
776+
.then(function() { return _drag(mid, [10, 10]);})
777+
.then(function() { _assertBase('from center to not far enough'); })
778+
.then(function() { return _drag([mid[0] + 30, mid[0] - 30], [-10, 0]);})
779+
.then(function() { _assertBase('from quadrant #1 to not far enough'); })
780+
.then(function() { return _drag([345, 200], [-10, 0]);})
781+
.then(function() { _assertBase('from right edge to not far enough'); })
782+
.then(function() {
783+
expect(eventCnts.plotly_relayout)
784+
.toBe(relayoutNumber, 'no new relayout events after *not far enough* cases');
785+
})
786+
.catch(fail)
787+
.then(done);
788+
});
789+
790+
it('should response to drag interactions on radial drag area', function(done) {
791+
var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json'));
792+
793+
// adjust margins so that middle of plot area is at 300x300
794+
// with its middle at [200,200]
795+
fig.layout.width = 400;
796+
fig.layout.height = 400;
797+
fig.layout.margin = {l: 50, t: 50, b: 50, r: 50};
798+
799+
var dragPos0 = [375, 200];
800+
var resetNumber = 0;
801+
802+
// use 'special' drag method - as we need two mousemove events
803+
// to activate the radial drag mode
804+
function _drag(p0, dp) {
805+
var node = d3.select('.polar > .draglayer > .radialdrag').node();
806+
var p1 = [p0[0] + dp[0] / 2, p0[1] + dp[1] / 2];
807+
var p2 = [p0[0] + dp[0], p0[1] + dp[1]];
808+
809+
mouseEvent('mousemove', p0[0], p0[1], {element: node});
810+
mouseEvent('mousedown', p0[0], p0[1], {element: node});
811+
812+
return delay(250)()
813+
.then(function() { mouseEvent('mousemove', p1[0], p1[1], {element: document}); })
814+
.then(delay(50))
815+
.then(function() { mouseEvent('mousemove', p2[0], p2[1], {element: document}); })
816+
.then(function() { mouseEvent('mouseup', p2[0], p2[1], {element: document}); })
817+
.then(delay(50));
818+
}
819+
820+
function _assert(rng, angle, evtRng1, evtAngle, msg) {
821+
expect(gd._fullLayout.polar.radialaxis.range)
822+
.toBeCloseToArray(rng, 1, msg + ' - range');
823+
expect(gd._fullLayout.polar.radialaxis.angle)
824+
.toBeCloseTo(angle, 1, msg + ' - angle');
825+
826+
if(evtRng1 !== null) {
827+
expect(eventData['polar.radialaxis.range[1]'])
828+
.toBeCloseTo(evtRng1, 1, msg + ' - range[1] event data');
829+
}
830+
if(evtAngle !== null) {
831+
expect(eventData['polar.radialaxis.angle'])
832+
.toBeCloseTo(evtAngle, 1, msg + ' - angle event data');
833+
}
834+
}
835+
836+
function _assertBase(extra) {
837+
extra = extra ? ' ' + extra : '';
838+
_assert([0, 11.1], 0, null, null, 'base' + extra);
839+
}
840+
841+
function _reset() {
842+
return _doubleClick([200, 200]).then(function() {
843+
resetNumber++;
844+
845+
var extra = '(reset ' + resetNumber + ')';
846+
_assertBase(extra);
847+
expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
848+
});
849+
}
850+
851+
_plot(fig)
852+
.then(_assertBase)
853+
.then(function() { return _drag(dragPos0, [-50, 0]); })
854+
.then(function() {
855+
_assert([0, 13.9], 0, 13.9, null, 'move inward');
856+
})
857+
.then(_reset)
858+
.then(function() { return _drag(dragPos0, [50, 0]); })
859+
.then(function() {
860+
_assert([0, 8.33], 0, 8.33, null, 'move outward');
861+
})
862+
.then(_reset)
863+
.then(function() { return _drag(dragPos0, [0, -50]); })
864+
.then(function() {
865+
_assert([0, 11.1], 15.94, null, 15.94, 'move counterclockwise');
866+
})
867+
.then(_reset)
868+
.then(function() { return _drag(dragPos0, [0, 50]); })
869+
.then(function() {
870+
_assert([0, 11.1], -15.94, null, -15.94, 'move clockwise');
871+
})
872+
.then(_reset)
873+
.then(function() {
874+
expect(eventCnts.plotly_relayout).toBe(8, 'total # of relayout events');
875+
})
876+
.catch(fail)
877+
.then(done);
878+
});
879+
880+
it('should response to drag interactions on angular drag area', function(done) {
881+
var fig = Lib.extendDeep({}, require('@mocks/polar_scatter.json'));
882+
883+
// adjust margins so that middle of plot area is at 300x300
884+
// with its middle at [200,200]
885+
fig.layout.width = 400;
886+
fig.layout.height = 400;
887+
fig.layout.margin = {l: 50, t: 50, b: 50, r: 50};
888+
889+
var dragPos0 = [350, 150];
890+
var resetNumber = 0;
891+
892+
function _drag(p0, dp) {
893+
var node = d3.select('.polar > .draglayer > .angulardrag').node();
894+
return drag(node, dp[0], dp[1], null, p0[0], p0[1]);
895+
}
896+
897+
function _assert(rot, msg, noEvent) {
898+
expect(gd._fullLayout.polar.angularaxis.rotation)
899+
.toBeCloseTo(rot, 1, msg + ' - rotation');
900+
if(!noEvent) {
901+
expect(eventData['polar.angularaxis.rotation'])
902+
.toBeCloseTo(rot, 1, msg + ' - rotation event data');
903+
}
904+
}
905+
906+
function _assertBase(extra) {
907+
extra = extra ? ' ' + extra : '';
908+
_assert(0, 'base' + extra, true);
909+
}
910+
911+
function _reset() {
912+
return _doubleClick([200, 200]).then(function() {
913+
resetNumber++;
914+
915+
var extra = '(reset ' + resetNumber + ')';
916+
_assertBase(extra);
917+
expect(eventCnts.plotly_doubleclick).toBe(resetNumber, 'doubleclick event #' + extra);
918+
});
919+
}
920+
921+
_plot(fig)
922+
.then(_assertBase)
923+
.then(function() { return _drag(dragPos0, [-20, -20]); })
924+
.then(function() {
925+
_assert(7.7, 'move counterclockwise');
926+
})
927+
.then(_reset)
928+
.then(function() { return _drag(dragPos0, [20, 20]); })
929+
.then(function() {
930+
_assert(-6.3, 'move clockwise');
931+
})
932+
.then(_reset)
933+
.then(function() {
934+
expect(eventCnts.plotly_relayout).toBe(4, 'total # of relayout events');
935+
})
936+
.catch(fail)
937+
.then(done);
938+
});
701939
});

0 commit comments

Comments
 (0)