Skip to content

Commit

Permalink
Merge pull request #4687 from plotly/fix-4693
Browse files Browse the repository at this point in the history
unified hover: honor layout.hoverlabel
  • Loading branch information
antoinerg authored Mar 26, 2020
2 parents 4849269 + 0d406a0 commit b4a395f
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 16 deletions.
7 changes: 4 additions & 3 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -989,9 +989,10 @@ function createHoverText(hoverData, opts, gd) {
var mockLayoutIn = {
showlegend: true,
legend: {
title: {text: t0, font: fullLayout.font},
font: fullLayout.font,
bgcolor: fullLayout.paper_bgcolor,
title: {text: t0, font: fullLayout.hoverlabel.font},
font: fullLayout.hoverlabel.font,
bgcolor: fullLayout.hoverlabel.bgcolor,
bordercolor: fullLayout.hoverlabel.bordercolor,
borderwidth: 1,
tracegroupgap: 7,
traceorder: fullLayout.legend ? fullLayout.legend.traceorder : undefined,
Expand Down
23 changes: 23 additions & 0 deletions src/components/fx/hoverlabel_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,33 @@
'use strict';

var Lib = require('../../lib');
var Color = require('../color');
var isUnifiedHover = require('./helpers').isUnifiedHover;

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

function inheritFontAttr(attr) {
if(!opts.font[attr]) {
opts.font[attr] = contOut.legend ? contOut.legend.font[attr] : contOut.font[attr];
}
}

// In unified hover, inherit from layout.legend if available or layout
if(contOut && isUnifiedHover(contOut.hovermode)) {
if(!opts.font) opts.font = {};
inheritFontAttr('size');
inheritFontAttr('family');
inheritFontAttr('color');

if(contOut.legend) {
if(!opts.bgcolor) opts.bgcolor = Color.combine(contOut.legend.bgcolor, contOut.paper_bgcolor);
if(!opts.bordercolor) opts.bordercolor = contOut.legend.bordercolor;
} else {
if(!opts.bgcolor) opts.bgcolor = contOut.paper_bgcolor;
}
}

coerce('hoverlabel.bgcolor', opts.bgcolor);
coerce('hoverlabel.bordercolor', opts.bordercolor);
coerce('hoverlabel.namelength', opts.namelength);
Expand Down
3 changes: 3 additions & 0 deletions src/components/fx/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var Lib = require('../../lib');
var isUnifiedHover = require('./helpers').isUnifiedHover;
var layoutAttributes = require('./layout_attributes');
var handleHoverModeDefaults = require('./hovermode_defaults');
var handleHoverLabelDefaults = require('./hoverlabel_defaults');

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
function coerce(attr, dflt) {
Expand Down Expand Up @@ -40,4 +41,6 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
)) {
layoutOut.dragmode = 'pan';
}

handleHoverLabelDefaults(layoutIn, layoutOut, coerce);
};
2 changes: 1 addition & 1 deletion src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ register(require('./traces/scatter'));

// register all registrable components modules
register([
require('./components/fx'),
require('./components/legend'),
require('./components/fx'), // fx needs to come after legend
require('./components/annotations'),
require('./components/annotations3d'),
require('./components/shapes'),
Expand Down
172 changes: 160 additions & 12 deletions test/jasmine/tests/hover_label_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3988,14 +3988,18 @@ describe('hovermode: (x|y)unified', function() {
Lib.clearThrottle();
}

function getHoverLabel() {
var hoverLayer = d3.select('g.hoverlayer');
return hoverLayer.select('g.legend');
}

function assertElementCount(selector, size) {
var g = d3.selectAll(selector);
expect(g.size()).toBe(size);
}

function assertLabel(expectation) {
var hoverLayer = d3.select('g.hoverlayer');
var hover = hoverLayer.select('g.legend');
var hover = getHoverLabel();
var title = hover.select('text.legendtitletext');
var traces = hover.selectAll('g.traces');

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

function assertBgcolor(color) {
var hoverLayer = d3.select('g.hoverlayer');
var hover = hoverLayer.select('g.legend');
function assertRectColor(color, bordercolor) {
var hover = getHoverLabel();
var bg = hover.select('rect.bg');
expect(bg.node().style.fill).toBe(color);
if(color) expect(bg.node().style.fill).toBe(color);
if(bordercolor) expect(bg.node().style.stroke).toBe(bordercolor);
}

function assertSymbol(exp) {
var hoverLayer = d3.select('g.hoverlayer');
var hover = hoverLayer.select('g.legend');
var hover = getHoverLabel();
var traces = hover.selectAll('g.traces');
expect(traces.size()).toBe(exp.length);

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

function assertFont(fontFamily, fontSize, fontColor) {
var hover = getHoverLabel();
var text = hover.select('text.legendtext');
var node = text.node();

var textStyle = window.getComputedStyle(node);
expect(textStyle.fontFamily.split(',')[0]).toBe(fontFamily, 'wrong font family');
expect(textStyle.fontSize).toBe(fontSize, 'wrong font size');
expect(textStyle.fill).toBe(fontColor, 'wrong font color');
}

it('set smart defaults for spikeline in x unified', function(done) {
Plotly.newPlot(gd, [{y: [4, 6, 5]}], {'hovermode': 'x unified', 'xaxis': {'color': 'red'}})
.then(function(gd) {
Expand Down Expand Up @@ -4316,15 +4330,149 @@ describe('hovermode: (x|y)unified', function() {
.then(done);
});

it('label should have color of paper_bgcolor', function(done) {
it('label should have bgcolor/bordercolor from hoverlabel or legend or paper_bgcolor', function(done) {
var mockCopy = Lib.extendDeep({}, mock);
var bgcolor = [
'rgb(10, 10, 10)',
'rgb(20, 20, 20)',
'rgb(30, 30, 30)',
'rgb(40, 40, 40)'
];

Plotly.newPlot(gd, mockCopy)
.then(function(gd) {
_hover(gd, { xval: 3 });

assertRectColor('rgb(255, 255, 255)', 'rgb(68, 68, 68)');

// Set paper_bgcolor
return Plotly.relayout(gd, 'paper_bgcolor', bgcolor[0]);
})
.then(function(gd) {
_hover(gd, { xval: 3 });

assertRectColor(bgcolor[0]);

// Set legend.bgcolor which should win over paper_bgcolor
return Plotly.relayout(gd, {
'showlegend': true,
'legend.bgcolor': bgcolor[1],
'legend.bordercolor': bgcolor[1]
});
})
.then(function(gd) {
_hover(gd, { xval: 3 });

assertRectColor(bgcolor[1], bgcolor[1]);

// Set hoverlabel.bgcolor which should win over legend.bgcolor
return Plotly.relayout(gd, {
'hoverlabel.bgcolor': bgcolor[2],
'hoverlabel.bordercolor': bgcolor[2]
});
})
.then(function(gd) {
_hover(gd, { xval: 3 });

assertRectColor(bgcolor[2], bgcolor[2]);

// Check that a legend.bgcolor defined in template works
delete mockCopy.layout;
mockCopy.layout = {
hovermode: 'x unified',
template: { layout: { legend: { bgcolor: bgcolor[1], bordercolor: bgcolor[1] } } }
};

return Plotly.newPlot(gd, mockCopy);
})
.then(function(gd) {
_hover(gd, { xval: 3 });

assertRectColor(bgcolor[1], bgcolor[1]);

// Check that a hoverlabel.bgcolor defined in template wins
delete mockCopy.layout;
mockCopy.layout = {
hovermode: 'x unified',
template: { layout: { hoverlabel: { bgcolor: bgcolor[3], bordercolor: bgcolor[3] } } },
legend: { bgcolor: bgcolor[1], bordercolor: bgcolor[1] }
};

return Plotly.newPlot(gd, mockCopy);
})
.then(function(gd) {
_hover(gd, { xval: 3 });

assertRectColor(bgcolor[3], bgcolor[3]);
})
.catch(failTest)
.then(done);
});

it('should use hoverlabel.font or legend.font or layout.font', function(done) {
var mockCopy = Lib.extendDeep({}, mock);
var bgcolor = 'rgb(15, 200, 85)';
mockCopy.layout.paper_bgcolor = bgcolor;

// Set layout.font
mockCopy.layout.font = {size: 20, family: 'Mono', color: 'rgb(10, 10, 10)'};
Plotly.newPlot(gd, mockCopy)
.then(function(gd) {
_hover(gd, { xval: 3});

assertFont('Mono', '20px', 'rgb(10, 10, 10)');

// Set legend.font which should win over layout font
return Plotly.relayout(gd, {
'showlegend': true,
'legend.font.size': 15,
'legend.font.family': 'Helvetica',
'legend.font.color': 'rgb(20, 20, 20)'
});
})
.then(function(gd) {
_hover(gd, { xval: 3 });

assertFont('Helvetica', '15px', 'rgb(20, 20, 20)');

// Set hoverlabel.font which should win over legend.font
return Plotly.relayout(gd, {
'hoverlabel.font.size': 22,
'hoverlabel.font.family': 'Arial',
'hoverlabel.font.color': 'rgb(30, 30, 30)'
});
})
.then(function() {
_hover(gd, { xval: 3 });

assertFont('Arial', '22px', 'rgb(30, 30, 30)');

// Check that a legend.font defined in template wins
delete mockCopy.layout;
mockCopy.layout = {
hovermode: 'x unified',
template: { layout: { legend: {font: {size: 5, family: 'Mono', color: 'rgb(5, 5, 5)'}}}},
};

return Plotly.newPlot(gd, mockCopy);
})
.then(function() {
_hover(gd, { xval: 3 });

assertFont('Mono', '5px', 'rgb(5, 5, 5)');

// Finally, check that a hoverlabel.font defined in template wins
delete mockCopy.layout;
mockCopy.layout = {
hovermode: 'x unified',
template: { layout: { hoverlabel: { font: {family: 'Mono', size: 30, color: 'red'}}}},
legend: {font: {size: 20, family: 'Mono', color: 'rgb(10, 10, 10)'}}
};

return Plotly.newPlot(gd, mockCopy);
})
.then(function() {
_hover(gd, { xval: 3 });

assertBgcolor(bgcolor);
assertFont('Mono', '30px', 'rgb(255, 0, 0)');
})
.catch(failTest)
.then(done);
Expand Down

0 comments on commit b4a395f

Please sign in to comment.