Skip to content

Commit b4a395f

Browse files
authored
Merge pull request #4687 from plotly/fix-4693
unified hover: honor layout.hoverlabel
2 parents 4849269 + 0d406a0 commit b4a395f

File tree

5 files changed

+191
-16
lines changed

5 files changed

+191
-16
lines changed

src/components/fx/hover.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -989,9 +989,10 @@ function createHoverText(hoverData, opts, gd) {
989989
var mockLayoutIn = {
990990
showlegend: true,
991991
legend: {
992-
title: {text: t0, font: fullLayout.font},
993-
font: fullLayout.font,
994-
bgcolor: fullLayout.paper_bgcolor,
992+
title: {text: t0, font: fullLayout.hoverlabel.font},
993+
font: fullLayout.hoverlabel.font,
994+
bgcolor: fullLayout.hoverlabel.bgcolor,
995+
bordercolor: fullLayout.hoverlabel.bordercolor,
995996
borderwidth: 1,
996997
tracegroupgap: 7,
997998
traceorder: fullLayout.legend ? fullLayout.legend.traceorder : undefined,

src/components/fx/hoverlabel_defaults.js

+23
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,33 @@
99
'use strict';
1010

1111
var Lib = require('../../lib');
12+
var Color = require('../color');
13+
var isUnifiedHover = require('./helpers').isUnifiedHover;
1214

1315
module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) {
1416
opts = opts || {};
1517

18+
function inheritFontAttr(attr) {
19+
if(!opts.font[attr]) {
20+
opts.font[attr] = contOut.legend ? contOut.legend.font[attr] : contOut.font[attr];
21+
}
22+
}
23+
24+
// In unified hover, inherit from layout.legend if available or layout
25+
if(contOut && isUnifiedHover(contOut.hovermode)) {
26+
if(!opts.font) opts.font = {};
27+
inheritFontAttr('size');
28+
inheritFontAttr('family');
29+
inheritFontAttr('color');
30+
31+
if(contOut.legend) {
32+
if(!opts.bgcolor) opts.bgcolor = Color.combine(contOut.legend.bgcolor, contOut.paper_bgcolor);
33+
if(!opts.bordercolor) opts.bordercolor = contOut.legend.bordercolor;
34+
} else {
35+
if(!opts.bgcolor) opts.bgcolor = contOut.paper_bgcolor;
36+
}
37+
}
38+
1639
coerce('hoverlabel.bgcolor', opts.bgcolor);
1740
coerce('hoverlabel.bordercolor', opts.bordercolor);
1841
coerce('hoverlabel.namelength', opts.namelength);

src/components/fx/layout_defaults.js

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var Lib = require('../../lib');
1212
var isUnifiedHover = require('./helpers').isUnifiedHover;
1313
var layoutAttributes = require('./layout_attributes');
1414
var handleHoverModeDefaults = require('./hovermode_defaults');
15+
var handleHoverLabelDefaults = require('./hoverlabel_defaults');
1516

1617
module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
1718
function coerce(attr, dflt) {
@@ -40,4 +41,6 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
4041
)) {
4142
layoutOut.dragmode = 'pan';
4243
}
44+
45+
handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
4346
};

src/core.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ register(require('./traces/scatter'));
4242

4343
// register all registrable components modules
4444
register([
45-
require('./components/fx'),
4645
require('./components/legend'),
46+
require('./components/fx'), // fx needs to come after legend
4747
require('./components/annotations'),
4848
require('./components/annotations3d'),
4949
require('./components/shapes'),

test/jasmine/tests/hover_label_test.js

+160-12
Original file line numberDiff line numberDiff line change
@@ -3988,14 +3988,18 @@ describe('hovermode: (x|y)unified', function() {
39883988
Lib.clearThrottle();
39893989
}
39903990

3991+
function getHoverLabel() {
3992+
var hoverLayer = d3.select('g.hoverlayer');
3993+
return hoverLayer.select('g.legend');
3994+
}
3995+
39913996
function assertElementCount(selector, size) {
39923997
var g = d3.selectAll(selector);
39933998
expect(g.size()).toBe(size);
39943999
}
39954000

39964001
function assertLabel(expectation) {
3997-
var hoverLayer = d3.select('g.hoverlayer');
3998-
var hover = hoverLayer.select('g.legend');
4002+
var hover = getHoverLabel();
39994003
var title = hover.select('text.legendtitletext');
40004004
var traces = hover.selectAll('g.traces');
40014005

@@ -4010,16 +4014,15 @@ describe('hovermode: (x|y)unified', function() {
40104014
});
40114015
}
40124016

4013-
function assertBgcolor(color) {
4014-
var hoverLayer = d3.select('g.hoverlayer');
4015-
var hover = hoverLayer.select('g.legend');
4017+
function assertRectColor(color, bordercolor) {
4018+
var hover = getHoverLabel();
40164019
var bg = hover.select('rect.bg');
4017-
expect(bg.node().style.fill).toBe(color);
4020+
if(color) expect(bg.node().style.fill).toBe(color);
4021+
if(bordercolor) expect(bg.node().style.stroke).toBe(bordercolor);
40184022
}
40194023

40204024
function assertSymbol(exp) {
4021-
var hoverLayer = d3.select('g.hoverlayer');
4022-
var hover = hoverLayer.select('g.legend');
4025+
var hover = getHoverLabel();
40234026
var traces = hover.selectAll('g.traces');
40244027
expect(traces.size()).toBe(exp.length);
40254028

@@ -4034,6 +4037,17 @@ describe('hovermode: (x|y)unified', function() {
40344037
});
40354038
}
40364039

4040+
function assertFont(fontFamily, fontSize, fontColor) {
4041+
var hover = getHoverLabel();
4042+
var text = hover.select('text.legendtext');
4043+
var node = text.node();
4044+
4045+
var textStyle = window.getComputedStyle(node);
4046+
expect(textStyle.fontFamily.split(',')[0]).toBe(fontFamily, 'wrong font family');
4047+
expect(textStyle.fontSize).toBe(fontSize, 'wrong font size');
4048+
expect(textStyle.fill).toBe(fontColor, 'wrong font color');
4049+
}
4050+
40374051
it('set smart defaults for spikeline in x unified', function(done) {
40384052
Plotly.newPlot(gd, [{y: [4, 6, 5]}], {'hovermode': 'x unified', 'xaxis': {'color': 'red'}})
40394053
.then(function(gd) {
@@ -4316,15 +4330,149 @@ describe('hovermode: (x|y)unified', function() {
43164330
.then(done);
43174331
});
43184332

4319-
it('label should have color of paper_bgcolor', function(done) {
4333+
it('label should have bgcolor/bordercolor from hoverlabel or legend or paper_bgcolor', function(done) {
4334+
var mockCopy = Lib.extendDeep({}, mock);
4335+
var bgcolor = [
4336+
'rgb(10, 10, 10)',
4337+
'rgb(20, 20, 20)',
4338+
'rgb(30, 30, 30)',
4339+
'rgb(40, 40, 40)'
4340+
];
4341+
4342+
Plotly.newPlot(gd, mockCopy)
4343+
.then(function(gd) {
4344+
_hover(gd, { xval: 3 });
4345+
4346+
assertRectColor('rgb(255, 255, 255)', 'rgb(68, 68, 68)');
4347+
4348+
// Set paper_bgcolor
4349+
return Plotly.relayout(gd, 'paper_bgcolor', bgcolor[0]);
4350+
})
4351+
.then(function(gd) {
4352+
_hover(gd, { xval: 3 });
4353+
4354+
assertRectColor(bgcolor[0]);
4355+
4356+
// Set legend.bgcolor which should win over paper_bgcolor
4357+
return Plotly.relayout(gd, {
4358+
'showlegend': true,
4359+
'legend.bgcolor': bgcolor[1],
4360+
'legend.bordercolor': bgcolor[1]
4361+
});
4362+
})
4363+
.then(function(gd) {
4364+
_hover(gd, { xval: 3 });
4365+
4366+
assertRectColor(bgcolor[1], bgcolor[1]);
4367+
4368+
// Set hoverlabel.bgcolor which should win over legend.bgcolor
4369+
return Plotly.relayout(gd, {
4370+
'hoverlabel.bgcolor': bgcolor[2],
4371+
'hoverlabel.bordercolor': bgcolor[2]
4372+
});
4373+
})
4374+
.then(function(gd) {
4375+
_hover(gd, { xval: 3 });
4376+
4377+
assertRectColor(bgcolor[2], bgcolor[2]);
4378+
4379+
// Check that a legend.bgcolor defined in template works
4380+
delete mockCopy.layout;
4381+
mockCopy.layout = {
4382+
hovermode: 'x unified',
4383+
template: { layout: { legend: { bgcolor: bgcolor[1], bordercolor: bgcolor[1] } } }
4384+
};
4385+
4386+
return Plotly.newPlot(gd, mockCopy);
4387+
})
4388+
.then(function(gd) {
4389+
_hover(gd, { xval: 3 });
4390+
4391+
assertRectColor(bgcolor[1], bgcolor[1]);
4392+
4393+
// Check that a hoverlabel.bgcolor defined in template wins
4394+
delete mockCopy.layout;
4395+
mockCopy.layout = {
4396+
hovermode: 'x unified',
4397+
template: { layout: { hoverlabel: { bgcolor: bgcolor[3], bordercolor: bgcolor[3] } } },
4398+
legend: { bgcolor: bgcolor[1], bordercolor: bgcolor[1] }
4399+
};
4400+
4401+
return Plotly.newPlot(gd, mockCopy);
4402+
})
4403+
.then(function(gd) {
4404+
_hover(gd, { xval: 3 });
4405+
4406+
assertRectColor(bgcolor[3], bgcolor[3]);
4407+
})
4408+
.catch(failTest)
4409+
.then(done);
4410+
});
4411+
4412+
it('should use hoverlabel.font or legend.font or layout.font', function(done) {
43204413
var mockCopy = Lib.extendDeep({}, mock);
4321-
var bgcolor = 'rgb(15, 200, 85)';
4322-
mockCopy.layout.paper_bgcolor = bgcolor;
4414+
4415+
// Set layout.font
4416+
mockCopy.layout.font = {size: 20, family: 'Mono', color: 'rgb(10, 10, 10)'};
43234417
Plotly.newPlot(gd, mockCopy)
43244418
.then(function(gd) {
4419+
_hover(gd, { xval: 3});
4420+
4421+
assertFont('Mono', '20px', 'rgb(10, 10, 10)');
4422+
4423+
// Set legend.font which should win over layout font
4424+
return Plotly.relayout(gd, {
4425+
'showlegend': true,
4426+
'legend.font.size': 15,
4427+
'legend.font.family': 'Helvetica',
4428+
'legend.font.color': 'rgb(20, 20, 20)'
4429+
});
4430+
})
4431+
.then(function(gd) {
4432+
_hover(gd, { xval: 3 });
4433+
4434+
assertFont('Helvetica', '15px', 'rgb(20, 20, 20)');
4435+
4436+
// Set hoverlabel.font which should win over legend.font
4437+
return Plotly.relayout(gd, {
4438+
'hoverlabel.font.size': 22,
4439+
'hoverlabel.font.family': 'Arial',
4440+
'hoverlabel.font.color': 'rgb(30, 30, 30)'
4441+
});
4442+
})
4443+
.then(function() {
4444+
_hover(gd, { xval: 3 });
4445+
4446+
assertFont('Arial', '22px', 'rgb(30, 30, 30)');
4447+
4448+
// Check that a legend.font defined in template wins
4449+
delete mockCopy.layout;
4450+
mockCopy.layout = {
4451+
hovermode: 'x unified',
4452+
template: { layout: { legend: {font: {size: 5, family: 'Mono', color: 'rgb(5, 5, 5)'}}}},
4453+
};
4454+
4455+
return Plotly.newPlot(gd, mockCopy);
4456+
})
4457+
.then(function() {
4458+
_hover(gd, { xval: 3 });
4459+
4460+
assertFont('Mono', '5px', 'rgb(5, 5, 5)');
4461+
4462+
// Finally, check that a hoverlabel.font defined in template wins
4463+
delete mockCopy.layout;
4464+
mockCopy.layout = {
4465+
hovermode: 'x unified',
4466+
template: { layout: { hoverlabel: { font: {family: 'Mono', size: 30, color: 'red'}}}},
4467+
legend: {font: {size: 20, family: 'Mono', color: 'rgb(10, 10, 10)'}}
4468+
};
4469+
4470+
return Plotly.newPlot(gd, mockCopy);
4471+
})
4472+
.then(function() {
43254473
_hover(gd, { xval: 3 });
43264474

4327-
assertBgcolor(bgcolor);
4475+
assertFont('Mono', '30px', 'rgb(255, 0, 0)');
43284476
})
43294477
.catch(failTest)
43304478
.then(done);

0 commit comments

Comments
 (0)