Skip to content

Commit 13c1237

Browse files
authored
Merge pull request #1709 from plotly/scatter-id-selection-order
Selection re-ordering for scatter traces with `ids`
2 parents cea9455 + 9c35675 commit 13c1237

File tree

3 files changed

+151
-7
lines changed

3 files changed

+151
-7
lines changed

src/components/drawing/index.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ drawing.setRect = function(s, x, y, w, h) {
4747
s.call(drawing.setPosition, x, y).call(drawing.setSize, w, h);
4848
};
4949

50-
/** Translate / remove node
50+
/** Translate node
5151
*
5252
* @param {object} d : calcdata point item
5353
* @param {sel} sel : d3 selction of node to translate
@@ -56,7 +56,7 @@ drawing.setRect = function(s, x, y, w, h) {
5656
*
5757
* @return {boolean} :
5858
* true if selection got translated
59-
* false if selection got removed
59+
* false if selection could not get translated
6060
*/
6161
drawing.translatePoint = function(d, sel, xa, ya) {
6262
// put xp and yp into d if pixel scaling is already done
@@ -71,7 +71,6 @@ drawing.translatePoint = function(d, sel, xa, ya) {
7171
sel.attr('transform', 'translate(' + x + ',' + y + ')');
7272
}
7373
} else {
74-
sel.remove();
7574
return false;
7675
}
7776

src/traces/scatter/plot.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -419,17 +419,20 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
419419
var enter = join.enter().append('path')
420420
.classed('point', true);
421421

422-
enter.call(Drawing.pointStyle, trace)
423-
.call(Drawing.translatePoints, xa, ya, trace);
424-
425422
if(hasTransition) {
426-
enter.style('opacity', 0).transition()
423+
enter
424+
.call(Drawing.pointStyle, trace)
425+
.call(Drawing.translatePoints, xa, ya, trace)
426+
.style('opacity', 0)
427+
.transition()
427428
.style('opacity', 1);
428429
}
429430

430431
var markerScale = showMarkers && Drawing.tryColorscale(trace.marker, '');
431432
var lineScale = showMarkers && Drawing.tryColorscale(trace.marker, 'line');
432433

434+
join.order();
435+
433436
join.each(function(d) {
434437
var el = d3.select(this);
435438
var sel = transition(el);
@@ -441,6 +444,8 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
441444
if(trace.customdata) {
442445
el.classed('plotly-customdata', d.data !== null && d.data !== undefined);
443446
}
447+
} else {
448+
sel.remove();
444449
}
445450
});
446451

@@ -460,6 +465,8 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
460465
// it gets converted to mathjax
461466
join.enter().append('g').classed('textpoint', true).append('text');
462467

468+
join.order();
469+
463470
join.each(function(d) {
464471
var g = d3.select(this);
465472
var sel = transition(g.select('text'));

test/jasmine/tests/scatter_test.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,144 @@ describe('end-to-end scatter tests', function() {
437437
.catch(fail)
438438
.then(done);
439439
});
440+
441+
function _assertNodes(ptStyle, txContent) {
442+
var pts = d3.selectAll('.point');
443+
var txs = d3.selectAll('.textpoint');
444+
445+
expect(pts.size()).toEqual(ptStyle.length);
446+
expect(txs.size()).toEqual(txContent.length);
447+
448+
pts.each(function(_, i) {
449+
expect(d3.select(this).style('fill')).toEqual(ptStyle[i], 'pt ' + i);
450+
});
451+
452+
txs.each(function(_, i) {
453+
expect(d3.select(this).select('text').text()).toEqual(txContent[i], 'tx ' + i);
454+
});
455+
}
456+
457+
it('should reorder point and text nodes even when linked to ids (shuffle case)', function(done) {
458+
Plotly.plot(gd, [{
459+
x: [150, 350, 650],
460+
y: [100, 300, 600],
461+
text: ['apple', 'banana', 'clementine'],
462+
ids: ['A', 'B', 'C'],
463+
mode: 'markers+text',
464+
marker: {
465+
color: ['rgb(255, 0, 0)', 'rgb(0, 255, 0)', 'rgb(0, 0, 255)']
466+
},
467+
transforms: [{
468+
type: 'sort',
469+
enabled: false,
470+
target: [0, 1, 0]
471+
}]
472+
}])
473+
.then(function() {
474+
_assertNodes(
475+
['rgb(255, 0, 0)', 'rgb(0, 255, 0)', 'rgb(0, 0, 255)'],
476+
['apple', 'banana', 'clementine']
477+
);
478+
479+
return Plotly.restyle(gd, 'transforms[0].enabled', true);
480+
})
481+
.then(function() {
482+
_assertNodes(
483+
['rgb(255, 0, 0)', 'rgb(0, 0, 255)', 'rgb(0, 255, 0)'],
484+
['apple', 'clementine', 'banana']
485+
);
486+
487+
return Plotly.restyle(gd, 'transforms[0].enabled', false);
488+
})
489+
.then(function() {
490+
_assertNodes(
491+
['rgb(255, 0, 0)', 'rgb(0, 255, 0)', 'rgb(0, 0, 255)'],
492+
['apple', 'banana', 'clementine']
493+
);
494+
})
495+
.catch(fail)
496+
.then(done);
497+
});
498+
499+
it('should reorder point and text nodes even when linked to ids (add/remove case)', function(done) {
500+
Plotly.plot(gd, [{
501+
x: [150, 350, null, 600],
502+
y: [100, 300, null, 700],
503+
text: ['apple', 'banana', null, 'clementine'],
504+
ids: ['A', 'B', null, 'C'],
505+
mode: 'markers+text',
506+
marker: {
507+
color: ['rgb(255, 0, 0)', 'rgb(0, 255, 0)', null, 'rgb(0, 0, 255)']
508+
},
509+
transforms: [{
510+
type: 'filter',
511+
enabled: false,
512+
target: [1, 0, 0, 1],
513+
operation: '=',
514+
value: 1
515+
}]
516+
}])
517+
.then(function() {
518+
_assertNodes(
519+
['rgb(255, 0, 0)', 'rgb(0, 255, 0)', 'rgb(0, 0, 255)'],
520+
['apple', 'banana', 'clementine']
521+
);
522+
523+
return Plotly.restyle(gd, 'transforms[0].enabled', true);
524+
})
525+
.then(function() {
526+
_assertNodes(
527+
['rgb(255, 0, 0)', 'rgb(0, 0, 255)'],
528+
['apple', 'clementine']
529+
);
530+
531+
return Plotly.restyle(gd, 'transforms[0].enabled', false);
532+
})
533+
.then(function() {
534+
_assertNodes(
535+
['rgb(255, 0, 0)', 'rgb(0, 255, 0)', 'rgb(0, 0, 255)'],
536+
['apple', 'banana', 'clementine']
537+
);
538+
})
539+
.catch(fail)
540+
.then(done);
541+
});
542+
543+
it('should smoothly add/remove nodes tags with *ids* during animations', function(done) {
544+
Plotly.plot(gd, {
545+
data: [{
546+
mode: 'markers+text',
547+
y: [1, 2, 1],
548+
text: ['apple', 'banana', 'clementine'],
549+
ids: ['A', 'B', 'C'],
550+
marker: { color: ['red', 'green', 'blue'] }
551+
}],
552+
frames: [{
553+
data: [{
554+
y: [2, 1, 2],
555+
text: ['apple', 'banana', 'dragon fruit'],
556+
ids: ['A', 'C', 'D'],
557+
marker: { color: ['red', 'blue', 'yellow'] }
558+
}]
559+
}]
560+
})
561+
.then(function() {
562+
_assertNodes(
563+
['rgb(255, 0, 0)', 'rgb(0, 128, 0)', 'rgb(0, 0, 255)'],
564+
['apple', 'banana', 'clementine']
565+
);
566+
567+
return Plotly.animate(gd, null, {frame: {redraw: false}});
568+
})
569+
.then(function() {
570+
_assertNodes(
571+
['rgb(255, 0, 0)', 'rgb(0, 0, 255)', 'rgb(255, 255, 0)'],
572+
['apple', 'banana', 'dragon fruit']
573+
);
574+
})
575+
.catch(fail)
576+
.then(done);
577+
});
440578
});
441579

442580
describe('scatter hoverPoints', function() {

0 commit comments

Comments
 (0)