Skip to content

Commit

Permalink
better ordering of trace hoverlabels for matching positions
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcjohnson committed Sep 1, 2018
1 parent 4e71dfa commit 2fde3dc
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 5 deletions.
19 changes: 15 additions & 4 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -951,21 +951,32 @@ function createHoverText(hoverData, opts, gd) {
function hoverAvoidOverlaps(hoverData, ax, fullLayout) {
var nummoves = 0;

// make groups of touching points
var axSign = 1;

// make groups of touching points
var pointgroups = hoverData.map(function(d, i) {
var axis = d[ax];
var axIsX = axis._id.charAt(0) === 'x';
var rng = axis.range;
if(!i && rng && ((rng[0] > rng[1]) !== axIsX)) axSign = -1;
return [{
i: i,
traceIndex: d.trace.index,
dp: 0,
pos: d.pos,
posref: d.posref,
size: d.by * (axis._id.charAt(0) === 'x' ? YFACTOR : 1) / 2,
size: d.by * (axIsX ? YFACTOR : 1) / 2,
pmin: 0,
pmax: (axis._id.charAt(0) === 'x' ? fullLayout.width : fullLayout.height)
pmax: (axIsX ? fullLayout.width : fullLayout.height)
}];
})
.sort(function(a, b) {
return a[0].posref - b[0].posref;
return (a[0].posref - b[0].posref) ||
// for equal positions, sort trace indices increasing or decreasing
// depending on whether the axis is reversed or not... so stacked
// traces will generally keep their order even if one trace adds
// nothing to the stack.
(axSign * (b[0].traceIndex - a[0].traceIndex));
});

var donepositioning, topOverlap, bottomOverlap, i, j, pti, sumdp;
Expand Down
14 changes: 14 additions & 0 deletions test/jasmine/assets/custom_assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ exports.assertHoverLabelContent = function(expectation, msg) {
expect(ptCnt)
.toBe(expectation.name.length, ptMsg + ' # of visible labels');

var bboxes = [];
d3.selectAll(ptSelector).each(function(_, i) {
assertLabelContent(
d3.select(this).select('text.nums'),
Expand All @@ -133,7 +134,20 @@ exports.assertHoverLabelContent = function(expectation, msg) {
expectation.name[i],
ptMsg + ' (name ' + i + ')'
);
bboxes.push({bbox: this.getBoundingClientRect(), index: i});
});
if(expectation.vOrder) {
bboxes.sort(function(a, b) {
return (a.bbox.top + a.bbox.bottom - b.bbox.top - b.bbox.bottom) / 2;
});
expect(bboxes.map(function(d) { return d.index; })).toEqual(expectation.vOrder);
}
if(expectation.hOrder) {
bboxes.sort(function(a, b) {
return (b.bbox.left + b.bbox.right - a.bbox.left - a.bbox.right) / 2;
});
expect(bboxes.map(function(d) { return d.index; })).toEqual(expectation.hOrder);
}
} else {
if(expectation.nums) {
fail(ptMsg + ': expecting *nums* labels, did not find any.');
Expand Down
87 changes: 86 additions & 1 deletion test/jasmine/tests/hover_label_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,92 @@ describe('hover info', function() {
Lib.clearThrottle();
}

describe('\'hover info for x/y/z traces', function() {
describe('hover label order for stacked traces with zeros', function() {
var gd;
beforeEach(function() {
gd = createGraphDiv();
});

it('puts the top trace on top', function(done) {
Plotly.plot(gd, [
{y: [1, 2, 3], type: 'bar', name: 'a'},
{y: [2, 0, 1], type: 'bar', name: 'b'},
{y: [1, 0, 1], type: 'bar', name: 'c'},
{y: [2, 1, 0], type: 'bar', name: 'd'}
], {
width: 500,
height: 400,
margin: {l: 0, t: 0, r: 0, b: 0},
barmode: 'stack'
})
.then(function() {
_hover(gd, 250, 250);
assertHoverLabelContent({
nums: ['2', '0', '0', '1'],
name: ['a', 'b', 'c', 'd'],
// a, b, c are all in the same place but keep their order
// d is included mostly as a sanity check
vOrder: [3, 2, 1, 0],
axis: '1'
});

// reverse the axis, labels should reverse
return Plotly.relayout(gd, 'yaxis.range', gd.layout.yaxis.range.slice().reverse());
})
.then(function() {
_hover(gd, 250, 250);
assertHoverLabelContent({
nums: ['2', '0', '0', '1'],
name: ['a', 'b', 'c', 'd'],
vOrder: [0, 1, 2, 3],
axis: '1'
});
})
.catch(failTest)
.then(done);
});

it('puts the right trace on the right', function(done) {
Plotly.plot(gd, [
{x: [1, 2, 3], type: 'bar', name: 'a', orientation: 'h'},
{x: [2, 0, 1], type: 'bar', name: 'b', orientation: 'h'},
{x: [1, 0, 1], type: 'bar', name: 'c', orientation: 'h'},
{x: [2, 1, 0], type: 'bar', name: 'd', orientation: 'h'}
], {
width: 500,
height: 400,
margin: {l: 0, t: 0, r: 0, b: 0},
barmode: 'stack'
})
.then(function() {
_hover(gd, 250, 250);
assertHoverLabelContent({
nums: ['2', '0', '0', '1'],
name: ['a', 'b', 'c', 'd'],
// a, b, c are all in the same place but keep their order
// d is included mostly as a sanity check
hOrder: [3, 2, 1, 0],
axis: '1'
});

// reverse the axis, labels should reverse
return Plotly.relayout(gd, 'xaxis.range', gd.layout.xaxis.range.slice().reverse());
})
.then(function() {
_hover(gd, 250, 250);
assertHoverLabelContent({
nums: ['2', '0', '0', '1'],
name: ['a', 'b', 'c', 'd'],
hOrder: [0, 1, 2, 3],
axis: '1'
});
})
.catch(failTest)
.then(done);
});
});

describe('hover info for x/y/z traces', function() {
var gd;
beforeEach(function() {
gd = createGraphDiv();
Expand Down

0 comments on commit 2fde3dc

Please sign in to comment.