Skip to content

Rotate hover labels in a few more scenarios #3043

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 4 commits into from
Sep 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ function _hover(gd, evt, subplot, noHoverEvent) {
vLinePoint: null
};

// does subplot have one (or more) horizontal traces?
// This is used to determine whether we rotate the labels or not
var hasOneHorizontalTrace = false;

// Figure out what we're hovering on:
// mouse location or user-supplied data

Expand All @@ -245,8 +249,12 @@ function _hover(gd, evt, subplot, noHoverEvent) {
hovermode = 'array';
for(itemnum = 0; itemnum < evt.length; itemnum++) {
cd = gd.calcdata[evt[itemnum].curveNumber||0];
trace = cd[0].trace;
if(cd[0].trace.hoverinfo !== 'skip') {
searchData.push(cd);
if(trace.orientation === 'h') {
hasOneHorizontalTrace = true;
}
}
}
}
Expand All @@ -256,6 +264,9 @@ function _hover(gd, evt, subplot, noHoverEvent) {
trace = cd[0].trace;
if(trace.hoverinfo !== 'skip' && helpers.isTraceInSubplots(trace, subplots)) {
searchData.push(cd);
if(trace.orientation === 'h') {
hasOneHorizontalTrace = true;
}
}
}

Expand Down Expand Up @@ -577,9 +588,10 @@ function _hover(gd, evt, subplot, noHoverEvent) {

gd._hoverdata = newhoverdata;

// if there's more than one horz bar trace,
// rotate the labels so they don't overlap
var rotateLabels = hovermode === 'y' && searchData.length > 1;
var rotateLabels = (
(hovermode === 'y' && (searchData.length > 1 || hoverData.length > 1)) ||
(hovermode === 'closest' && hasOneHorizontalTrace && hoverData.length > 1)
);

var bgColor = Color.combine(
fullLayout.plot_bgcolor || Color.background,
Expand Down
51 changes: 31 additions & 20 deletions test/jasmine/assets/custom_assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ function count(selector) {
* - nums {string || array of strings}
* - name {string || array of strings}
* - axis {string}
* - vOrder {array of number}
* - hOrder {array of number}
* - isRotated {boolean}
* @param {string} msg
*/
exports.assertHoverLabelContent = function(expectation, msg) {
Expand All @@ -103,17 +106,22 @@ exports.assertHoverLabelContent = function(expectation, msg) {
var axMsg = 'common axis hover label';
var axCnt = count(axSelector);

var reRotate = /(\brotate\(.*?\);?)/;

if(ptCnt === 1) {
assertLabelContent(
d3.select(ptSelector + '> text.nums'),
expectation.nums,
ptMsg + ' (nums)'
);
assertLabelContent(
d3.select(ptSelector + '> text.name'),
expectation.name,
ptMsg + ' (name)'
);
var g = d3.select(ptSelector);
var numsSel = g.select('text.nums');
var nameSel = g.select('text.name');

assertLabelContent(numsSel, expectation.nums, ptMsg + ' (nums)');
assertLabelContent(nameSel, expectation.name, ptMsg + ' (name)');

if('isRotated' in expectation) {
expect(g.attr('transform').match(reRotate))
.negateIf(expectation.isRotated)
.toBe(null, ptMsg + ' should be rotated');

}
} else if(ptCnt > 1) {
if(!Array.isArray(expectation.nums) || !Array.isArray(expectation.name)) {
fail(ptMsg + ': expecting more than 1 labels.');
Expand All @@ -124,16 +132,19 @@ exports.assertHoverLabelContent = function(expectation, msg) {

var bboxes = [];
d3.selectAll(ptSelector).each(function(_, i) {
assertLabelContent(
d3.select(this).select('text.nums'),
expectation.nums[i],
ptMsg + ' (nums ' + i + ')'
);
assertLabelContent(
d3.select(this).select('text.name'),
expectation.name[i],
ptMsg + ' (name ' + i + ')'
);
var g = d3.select(this);
var numsSel = g.select('text.nums');
var nameSel = g.select('text.name');

assertLabelContent(numsSel, expectation.nums[i], ptMsg + ' (nums ' + i + ')');
assertLabelContent(nameSel, expectation.name[i], ptMsg + ' (name ' + i + ')');

if('isRotated' in expectation) {
expect(g.attr('transform').match(reRotate))
.negateIf(expectation.isRotated)
.toBe(null, ptMsg + ' ' + i + ' should be rotated');
}

bboxes.push({bbox: this.getBoundingClientRect(), index: i});
});
if(expectation.vOrder) {
Expand Down
107 changes: 107 additions & 0 deletions test/jasmine/tests/hover_label_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2470,6 +2470,113 @@ describe('hover distance', function() {
});
});

describe('hover label rotation:', function() {
var gd;

function _hover(gd, opts) {
Fx.hover(gd, opts);
Lib.clearThrottle();
}

describe('when a single pt is picked', function() {
afterAll(destroyGraphDiv);

beforeAll(function(done) {
gd = createGraphDiv();

Plotly.plot(gd, [{
type: 'bar',
orientation: 'h',
y: [0, 1, 2],
x: [1, 2, 1]
}, {
type: 'bar',
orientation: 'h',
y: [3, 4, 5],
x: [1, 2, 1]
}], {
hovermode: 'y'
})
.catch(failTest)
.then(done);
});

it('should rotate labels under *hovermode:y*', function() {
_hover(gd, { xval: 2, yval: 1 });
assertHoverLabelContent({
nums: '2',
name: 'trace 0',
axis: '1',
// N.B. could be changed to be made consistent with 'closest'
isRotated: true
});
});

it('should not rotate labels under *hovermode:closest*', function(done) {
Plotly.relayout(gd, 'hovermode', 'closest').then(function() {
_hover(gd, { xval: 1.9, yval: 1 });
assertHoverLabelContent({
nums: '(2, 1)',
name: 'trace 0',
axis: '',
isRotated: false
});
})
.catch(failTest)
.then(done);
});
});

describe('when mulitple pts are picked', function() {
afterAll(destroyGraphDiv);

beforeAll(function(done) {
gd = createGraphDiv();

Plotly.plot(gd, [{
type: 'bar',
orientation: 'h',
y: [0, 1, 2],
x: [1, 2, 1]
}, {
type: 'bar',
orientation: 'h',
y: [0, 1, 2],
x: [1, 2, 1]
}], {
hovermode: 'y'
})
.catch(failTest)
.then(done);
});

it('should rotate labels under *hovermode:y*', function() {
_hover(gd, { xval: 2, yval: 1 });
assertHoverLabelContent({
nums: ['2', '2'],
name: ['trace 0', 'trace 1'],
axis: '1',
isRotated: true
});
});

it('should not rotate labels under *hovermode:closest*', function(done) {
Plotly.relayout(gd, 'hovermode', 'closest').then(function() {
_hover(gd, { xval: 1.9, yval: 1 });
assertHoverLabelContent({
nums: '(2, 1)',
// N.B. only showing the 'top' trace
name: 'trace 1',
axis: '',
isRotated: false
});
})
.catch(failTest)
.then(done);
});
});
});

describe('hovermode defaults to', function() {
var gd;

Expand Down
58 changes: 58 additions & 0 deletions test/jasmine/tests/violin_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,64 @@ describe('Test violin hover:', function() {
nums: 'x: 42.43046, kde: 0.083',
name: '',
axis: 'Saturday'
}, {
desc: 'single horizontal violin',
mock: require('@mocks/violin_non-linear.json'),
pos: [310, 160],
nums: ['median: C', 'min: A', 'q1: B', 'q3: D', 'max: G', 'upper fence: D', 'x: C, kde: 1.005'],
name: ['categories', '', '', '', '', '', ''],
axis: 'categories',
hOrder: [4, 5, 3, 6, 0, 2, 1],
isRotated: true
}, {
desc: 'multiple horizontal violins',
mock: require('@mocks/box_grouped_horz.json'),
patch: function(fig) {
fig.data.forEach(function(t) {
t.type = 'violin';
t.hoveron = 'violins';
});
fig.layout.violinmode = 'group';
return fig;
},
nums: ['median: 0.4', 'min: 0.1', 'q1: 0.2', 'q3: 0.7', 'max: 0.9'],
name: ['kale', '', '', '', ''],
axis: 'day 2',
hOrder: [4, 3, 0, 2, 1],
isRotated: true
}, {
desc: 'multiple horizontal violins (under hovermode:closest)',
mock: require('@mocks/box_grouped_horz.json'),
patch: function(fig) {
fig.data.forEach(function(t) {
t.type = 'violin';
t.hoveron = 'violins';
});
fig.layout.violinmode = 'group';
fig.layout.hovermode = 'closest';
return fig;
},
pos: [200, 175],
nums: [
'(median: 0.7, day 2)', '(min: 0.2, day 2)', '(q1: 0.5, day 2)',
'(q3: 0.8, day 2)', '(max: 0.9, day 2)'
],
name: ['radishes', '', '', '', ''],
hOrder: [4, 3, 0, 2, 1],
isRotated: true
}, {
desc: 'hovering over single pt on horizontal violin should not rotate labels',
mock: require('@mocks/violin_old-faithful.json'),
patch: function(fig) {
fig.data[0].x = fig.data[0].y;
delete fig.data[0].y;
fig.layout = {hovermode: 'closest'};
return fig;
},
pos: [539, 293],
nums: '(96, Old Faithful)',
name: '',
isRotated: false
}]
.forEach(function(specs) {
it('should generate correct hover labels ' + specs.desc, function(done) {
Expand Down