Skip to content

Align hover label text content with *hoverlabel.align* #3753

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
Apr 9, 2019
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
45 changes: 11 additions & 34 deletions src/components/fx/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,26 @@
'use strict';

var fontAttrs = require('../../plots/font_attributes');
var hoverLabelAttrs = require('./layout_attributes').hoverlabel;
var extendFlat = require('../../lib/extend').extendFlat;

module.exports = {
hoverlabel: {
bgcolor: {
valType: 'color',
role: 'style',
bgcolor: extendFlat({}, hoverLabelAttrs.bgcolor, {
arrayOk: true,
editType: 'none',
description: [
'Sets the background color of the hover labels for this trace'
].join(' ')
},
bordercolor: {
valType: 'color',
role: 'style',
description: 'Sets the background color of the hover labels for this trace'
}),
bordercolor: extendFlat({}, hoverLabelAttrs.bordercolor, {
arrayOk: true,
editType: 'none',
description: [
'Sets the border color of the hover labels for this trace.'
].join(' ')
},
description: 'Sets the border color of the hover labels for this trace.'
}),
font: fontAttrs({
arrayOk: true,
editType: 'none',
description: 'Sets the font used in hover labels.'
}),
namelength: {
valType: 'integer',
min: -1,
dflt: 15,
arrayOk: true,
role: 'style',
editType: 'none',
description: [
'Sets the length (in number of characters) of the trace name in',
'the hover labels for this trace. -1 shows the whole name',
'regardless of length. 0-3 shows the first 0-3 characters, and',
'an integer >3 will show the whole name if it is less than that',
'many characters, but if it is longer, will truncate to',
'`namelength - 3` characters and add an ellipsis.',
'Note that when `hovertemplate` is set, `namelength` defaults to *-1*.'
].join(' ')
},
editType: 'calc'
align: extendFlat({}, hoverLabelAttrs.align, {arrayOk: true}),
namelength: extendFlat({}, hoverLabelAttrs.namelength, {arrayOk: true}),
editType: 'none'
}
};
1 change: 1 addition & 0 deletions src/components/fx/calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = function calc(gd) {
fillFn(trace.hoverlabel.font.color, cd, 'htc');
fillFn(trace.hoverlabel.font.family, cd, 'htf');
fillFn(trace.hoverlabel.namelength, cd, 'hnl');
fillFn(trace.hoverlabel.align, cd, 'hta');
}
};

Expand Down
34 changes: 24 additions & 10 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ exports.loneHover = function loneHover(hoverItem, opts) {
fontSize: hoverItem.fontSize,
fontColor: hoverItem.fontColor,
nameLength: hoverItem.nameLength,
textAlign: hoverItem.textAlign,

// filler to make createHoverText happy
trace: hoverItem.trace || {
Expand Down Expand Up @@ -182,6 +183,7 @@ exports.multiHovers = function multiHovers(hoverItems, opts) {
fontSize: hoverItem.fontSize,
fontColor: hoverItem.fontColor,
nameLength: hoverItem.nameLength,
textAlign: hoverItem.textAlign,

// filler to make createHoverText happy
trace: hoverItem.trace || {
Expand Down Expand Up @@ -1281,20 +1283,18 @@ function alignHoverText(hoverLabels, rotateLabels) {
// box around it
hoverLabels.each(function(d) {
var g = d3.select(this);
if(d.del) {
g.remove();
return;
}
if(d.del) return g.remove();

var horzSign = d.anchor === 'end' ? -1 : 1;
var tx = g.select('text.nums');
var alignShift = {start: 1, end: -1, middle: 0}[d.anchor];
var anchor = d.anchor;
var horzSign = anchor === 'end' ? -1 : 1;
var alignShift = {start: 1, end: -1, middle: 0}[anchor];
var txx = alignShift * (HOVERARROWSIZE + HOVERTEXTPAD);
var tx2x = txx + alignShift * (d.txwidth + HOVERTEXTPAD);
var offsetX = 0;
var offsetY = d.offset;

if(d.anchor === 'middle') {
if(anchor === 'middle') {
txx -= d.tx2width / 2;
tx2x += d.txwidth / 2 + HOVERTEXTPAD;
}
Expand All @@ -1303,7 +1303,7 @@ function alignHoverText(hoverLabels, rotateLabels) {
offsetX = d.offset * YSHIFTX;
}

g.select('path').attr('d', d.anchor === 'middle' ?
g.select('path').attr('d', anchor === 'middle' ?
// middle aligned: rect centered on data
('M-' + (d.bx / 2 + d.tx2width / 2) + ',' + (offsetY - d.by / 2) +
'h' + d.bx + 'v' + d.by + 'h-' + d.bx + 'Z') :
Expand All @@ -1316,8 +1316,21 @@ function alignHoverText(hoverLabels, rotateLabels) {
'V' + (offsetY - HOVERARROWSIZE) +
'Z'));

tx.call(svgTextUtils.positionText,
txx + offsetX, offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD);
var posX = txx + offsetX;
var posY = offsetY + d.ty0 - d.by / 2 + HOVERTEXTPAD;
var textAlign = d.textAlign || 'auto';

if(textAlign !== 'auto') {
if(textAlign === 'left' && anchor !== 'start') {
tx.attr('text-anchor', 'start');
posX = -d.bx - HOVERTEXTPAD;
} else if(textAlign === 'right' && anchor !== 'end') {
tx.attr('text-anchor', 'end');
posX = d.bx + HOVERTEXTPAD;
}
}

tx.call(svgTextUtils.positionText, posX, posY);

if(d.tx2width) {
g.select('text.name')
Expand Down Expand Up @@ -1364,6 +1377,7 @@ function cleanPoint(d, hovermode) {
fill('fontSize', 'hts', 'hoverlabel.font.size');
fill('fontColor', 'htc', 'hoverlabel.font.color');
fill('nameLength', 'hnl', 'hoverlabel.namelength');
fill('textAlign', 'hta', 'hoverlabel.align');

d.posref = (hovermode === 'y' || (hovermode === 'closest' && trace.orientation === 'h')) ?
(d.xa._offset + (d.x0 + d.x1) / 2) :
Expand Down
1 change: 1 addition & 0 deletions src/components/fx/hoverlabel_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts
coerce('hoverlabel.bordercolor', opts.bordercolor);
coerce('hoverlabel.namelength', opts.namelength);
Lib.coerceFont(coerce, 'hoverlabel.font', opts.font);
coerce('hoverlabel.align', opts.align);
};
11 changes: 11 additions & 0 deletions src/components/fx/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ module.exports = {
].join(' ')
},
font: fontAttrs,
align: {
valType: 'enumerated',
values: ['left', 'right', 'auto'],
dflt: 'auto',
role: 'style',
editType: 'none',
description: [
'Sets the horizontal alignment of the text content within hover label box.',
'Has an effect only if the hover label text spans more two or more lines'
].join(' ')
},
namelength: {
valType: 'integer',
min: -1,
Expand Down
3 changes: 2 additions & 1 deletion src/plots/gl2d/scene2d.js
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,8 @@ proto.draw = function() {
fontFamily: Fx.castHoverOption(trace, ptNumber, 'font.family'),
fontSize: Fx.castHoverOption(trace, ptNumber, 'font.size'),
fontColor: Fx.castHoverOption(trace, ptNumber, 'font.color'),
nameLength: Fx.castHoverOption(trace, ptNumber, 'namelength')
nameLength: Fx.castHoverOption(trace, ptNumber, 'namelength'),
textAlign: Fx.castHoverOption(trace, ptNumber, 'align')
}, {
container: this.svgContainer,
gd: this.graphDiv
Expand Down
1 change: 1 addition & 0 deletions src/plots/gl3d/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ function render(scene) {
fontSize: Fx.castHoverOption(traceNow, ptNumber, 'font.size'),
fontColor: Fx.castHoverOption(traceNow, ptNumber, 'font.color'),
nameLength: Fx.castHoverOption(traceNow, ptNumber, 'namelength'),
textAlign: Fx.castHoverOption(traceNow, ptNumber, 'align'),
hovertemplate: Lib.castOption(traceNow, ptNumber, 'hovertemplate'),
hovertemplateLabels: Lib.extendFlat({}, pointData, labels),
eventData: [pointData]
Expand Down
1 change: 1 addition & 0 deletions src/traces/pie/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ function attachFxHandlers(sliceTop, gd, cd) {
fontSize: helpers.castOption(hoverFont.size, pt.pts),
fontColor: helpers.castOption(hoverFont.color, pt.pts),
nameLength: helpers.castOption(hoverLabel.namelength, pt.pts),
textAlign: helpers.castOption(hoverLabel.align, pt.pts),
hovertemplate: helpers.castOption(trace2.hovertemplate, pt.pts),
hovertemplateLabels: pt,
eventData: [eventData(pt, trace2)]
Expand Down
2 changes: 2 additions & 0 deletions src/traces/sankey/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ module.exports = function plot(gd, calcData) {
fontSize: castHoverOption(obj, 'font.size'),
fontColor: castHoverOption(obj, 'font.color'),
nameLength: castHoverOption(obj, 'namelength'),
textAlign: castHoverOption(obj, 'align'),
idealAlign: d3.event.x < hoverCenter[0] ? 'right' : 'left',

hovertemplate: obj.hovertemplate,
Expand Down Expand Up @@ -301,6 +302,7 @@ module.exports = function plot(gd, calcData) {
fontSize: castHoverOption(obj, 'font.size'),
fontColor: castHoverOption(obj, 'font.color'),
nameLength: castHoverOption(obj, 'namelength'),
textAlign: castHoverOption(obj, 'align'),
idealAlign: 'left',

hovertemplate: obj.hovertemplate,
Expand Down
1 change: 1 addition & 0 deletions src/traces/sunburst/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ function attachFxHandlers(sliceTop, gd, cd) {
fontSize: _cast('hoverlabel.font.size'),
fontColor: _cast('hoverlabel.font.color'),
nameLength: _cast('hoverlabel.namelength'),
textAlign: _cast('hoverlabel.align'),
hovertemplate: hovertemplate,
hovertemplateLabels: hoverPt,
eventData: [makeEventData(pt, traceNow)]
Expand Down
2 changes: 2 additions & 0 deletions test/jasmine/tests/fx_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ describe('Fx defaults', function() {
size: 40,
color: 'pink'
},
align: 'auto',
namelength: 15
});

Expand All @@ -182,6 +183,7 @@ describe('Fx defaults', function() {
size: 20,
color: 'red'
},
align: 'auto',
namelength: 15
});

Expand Down
48 changes: 48 additions & 0 deletions test/jasmine/tests/hover_label_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2034,6 +2034,54 @@ describe('hover info', function() {
.catch(failTest)
.then(done);
});

it('should honor *hoverlabel.align', function(done) {
var gd = createGraphDiv();

function _assert(msg, exp) {
var tx = d3.select('g.hovertext').select('text');
expect(tx.attr('text-anchor')).toBe(exp.textAnchor, 'text anchor|' + msg);
expect(Number(tx.attr('x'))).toBeWithin(exp.posX, 3, 'x position|' + msg);
}

Plotly.plot(gd, [{
y: [1, 2, 1],
text: 'LONG TEXT'
}], {
xaxis: {range: [0, 2]},
margin: {l: 0, t: 0, b: 0, r: 0},
hovermode: 'closest',
width: 400,
height: 400
})
.then(function() { _hoverNatural(gd, 0, 395); })
.then(function() { _assert('base left pt', {textAnchor: 'start', posX: 9}); })
.then(function() { _hoverNatural(gd, 395, 395); })
.then(function() { _assert('base right pt', {textAnchor: 'end', posX: -9}); })
.then(function() {
return Plotly.relayout(gd, 'hoverlabel.align', 'left');
})
.then(function() { _hoverNatural(gd, 0, 395); })
.then(function() { _assert('align:left left pt', {textAnchor: 'start', posX: 9}); })
.then(function() { _hoverNatural(gd, 395, 395); })
.then(function() { _assert('align:left right pt', {textAnchor: 'start', posX: -84.73}); })
.then(function() {
return Plotly.restyle(gd, 'hoverlabel.align', 'right');
})
.then(function() { _hoverNatural(gd, 0, 395); })
.then(function() { _assert('align:right left pt', {textAnchor: 'end', posX: 84.73}); })
.then(function() { _hoverNatural(gd, 395, 395); })
.then(function() { _assert('align:right right pt', {textAnchor: 'end', posX: -9}); })
.then(function() {
return Plotly.restyle(gd, 'hoverlabel.align', [['right', 'auto', 'left']]);
})
.then(function() { _hoverNatural(gd, 0, 395); })
.then(function() { _assert('arrayOk align:right left pt', {textAnchor: 'end', posX: 84.73}); })
.then(function() { _hoverNatural(gd, 395, 395); })
.then(function() { _assert('arrayOk align:left right pt', {textAnchor: 'start', posX: -84.73}); })
.catch(failTest)
.then(done);
});
});

describe('hover info on stacked subplots', function() {
Expand Down
55 changes: 55 additions & 0 deletions test/jasmine/tests/pie_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,61 @@ describe('pie hovering', function() {
.catch(failTest)
.then(done);
});

it('should honor *hoverlabel.align*', function(done) {
function _assert(msg, exp) {
var tx = d3.select('g.hovertext').select('text');
expect(tx.attr('text-anchor')).toBe(exp.textAnchor, 'text anchor|' + msg);
expect(Number(tx.attr('x'))).toBeWithin(exp.posX, 3, 'x position|' + msg);
}

function _hoverLeft() {
mouseEvent('mouseover', 100, 200);
Lib.clearThrottle();
}

function _hoverRight() {
mouseEvent('mouseover', 300, 200);
Lib.clearThrottle();
}

Plotly.plot(gd, [{
type: 'pie',
labels: ['a', 'b']
}], {
showlegend: false,
margin: {l: 0, t: 0, b: 0, r: 0},
width: 400,
height: 400
})
.then(_hoverLeft)
.then(function() { _assert('base left sector', {textAnchor: 'start', posX: 9}); })
.then(_hoverRight)
.then(function() { _assert('base right sector', {textAnchor: 'end', posX: -9}); })
.then(function() {
return Plotly.relayout(gd, 'hoverlabel.align', 'left');
})
.then(_hoverLeft)
.then(function() { _assert('align:left left sector', {textAnchor: 'start', posX: 9}); })
.then(_hoverRight)
.then(function() { _assert('align:left right sector', {textAnchor: 'start', posX: -37.45}); })
.then(function() {
return Plotly.restyle(gd, 'hoverlabel.align', 'right');
})
.then(_hoverLeft)
.then(function() { _assert('align:right left sector', {textAnchor: 'end', posX: 37.45}); })
.then(_hoverRight)
.then(function() { _assert('align:right right sector', {textAnchor: 'end', posX: -9}); })
.then(function() {
return Plotly.restyle(gd, 'hoverlabel.align', [['left', 'right']]);
})
.then(_hoverLeft)
.then(function() { _assert('arrayOk align:right left sector', {textAnchor: 'end', posX: 37.45}); })
.then(_hoverRight)
.then(function() { _assert('arrayOk align:left right sector', {textAnchor: 'start', posX: -37.45}); })
.catch(failTest)
.then(done);
});
});

describe('should fit labels within graph div', function() {
Expand Down