Skip to content

Commit aab3366

Browse files
committed
implement arrayOk hoverinfo in Fx.hover
- make base hoverinfo `arrayOk: true` - merge array hoverinfo in calcdata while coercing its flags - look for per-point hoverinfo, fallback to trace hoverinfo - add checks string (vs array) hoverinfo values
1 parent bc0e06d commit aab3366

File tree

7 files changed

+117
-40
lines changed

7 files changed

+117
-40
lines changed

src/components/fx/calc.js

+29-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,33 @@
1010

1111
var Lib = require('../../lib');
1212
var Registry = require('../../registry');
13+
var baseAttrs = require('../../plots/attributes');
1314

1415
module.exports = function calc(gd) {
1516
var calcdata = gd.calcdata;
17+
var fullLayout = gd._fullLayout;
18+
19+
function makeCoerceHoverInfo(fullTrace) {
20+
var moduleAttrs = fullTrace._module.attributes;
21+
var attrs = moduleAttrs.hoverinfo ?
22+
{hoverinfo: moduleAttrs.hoverinfo} :
23+
baseAttrs;
24+
var valObj = attrs.hoverinfo;
25+
var dflt;
26+
27+
if(fullLayout._dataLength === 1) {
28+
var flags = valObj.dflt === 'all' ?
29+
valObj.flags.slice() :
30+
valObj.dflt.split('+');
31+
32+
flags.splice(flags.indexOf('name'), 1);
33+
dflt = flags.join('+');
34+
}
35+
36+
return function(val) {
37+
return Lib.coerce({hoverinfo: val}, {}, attrs, 'hoverinfo', dflt);
38+
};
39+
}
1640

1741
for(var i = 0; i < calcdata.length; i++) {
1842
var cd = calcdata[i];
@@ -22,6 +46,7 @@ module.exports = function calc(gd) {
2246

2347
var mergeFn = Registry.traceIs(trace, '2dMap') ? paste : Lib.mergeArray;
2448

49+
mergeFn(trace.hoverinfo, cd, 'hi', makeCoerceHoverInfo(trace));
2550
mergeFn(trace.hoverlabel.bgcolor, cd, 'hbg');
2651
mergeFn(trace.hoverlabel.bordercolor, cd, 'hbc');
2752
mergeFn(trace.hoverlabel.font.size, cd, 'hts');
@@ -30,8 +55,10 @@ module.exports = function calc(gd) {
3055
}
3156
};
3257

33-
function paste(traceAttr, cd, cdAttr) {
58+
function paste(traceAttr, cd, cdAttr, fn) {
59+
fn = fn || Lib.identity;
60+
3461
if(Array.isArray(traceAttr)) {
35-
cd[0][cdAttr] = traceAttr;
62+
cd[0][cdAttr] = fn(traceAttr);
3663
}
3764
}

src/components/fx/hover.js

+26-25
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ function createHoverText(hoverData, opts) {
558558
// to have common labels
559559
var i, traceHoverinfo;
560560
for(i = 0; i < hoverData.length; i++) {
561-
traceHoverinfo = hoverData[i].trace.hoverinfo;
561+
traceHoverinfo = hoverData[i].hoverinfo || hoverData[i].trace.hoverinfo;
562562
var parts = traceHoverinfo.split('+');
563563
if(parts.indexOf('all') === -1 &&
564564
parts.indexOf(hovermode) === -1) {
@@ -1056,6 +1056,30 @@ function cleanPoint(d, hovermode) {
10561056
var cd0 = d.cd[0];
10571057
var cd = d.cd[d.index] || {};
10581058

1059+
function fill(key, calcKey, traceKey) {
1060+
var val;
1061+
1062+
if(cd[calcKey]) {
1063+
val = cd[calcKey];
1064+
} else if(cd0[calcKey]) {
1065+
var arr = cd0[calcKey];
1066+
if(Array.isArray(arr) && Array.isArray(arr[d.index[0]])) {
1067+
val = arr[d.index[0]][d.index[1]];
1068+
}
1069+
} else {
1070+
val = Lib.nestedProperty(trace, traceKey).get();
1071+
}
1072+
1073+
if(val) d[key] = val;
1074+
}
1075+
1076+
fill('hoverinfo', 'hi', 'hoverinfo');
1077+
fill('color', 'hbg', 'hoverlabel.bgcolor');
1078+
fill('borderColor', 'hbc', 'hoverlabel.bordercolor');
1079+
fill('fontFamily', 'htf', 'hoverlabel.font.family');
1080+
fill('fontSize', 'hts', 'hoverlabel.font.size');
1081+
fill('fontColor', 'htc', 'hoverlabel.font.color');
1082+
10591083
d.posref = hovermode === 'y' ? (d.x0 + d.x1) / 2 : (d.y0 + d.y1) / 2;
10601084

10611085
// then constrain all the positions to be on the plot
@@ -1123,7 +1147,7 @@ function cleanPoint(d, hovermode) {
11231147
if(hovermode === 'y') d.distance += 1;
11241148
}
11251149

1126-
var infomode = d.trace.hoverinfo;
1150+
var infomode = d.hoverinfo || d.trace.hoverinfo;
11271151
if(infomode !== 'all') {
11281152
infomode = infomode.split('+');
11291153
if(infomode.indexOf('x') === -1) d.xLabel = undefined;
@@ -1133,29 +1157,6 @@ function cleanPoint(d, hovermode) {
11331157
if(infomode.indexOf('name') === -1) d.name = undefined;
11341158
}
11351159

1136-
function fill(key, calcKey, traceKey) {
1137-
var val;
1138-
1139-
if(cd[calcKey]) {
1140-
val = cd[calcKey];
1141-
} else if(cd0[calcKey]) {
1142-
var arr = cd0[calcKey];
1143-
if(Array.isArray(arr) && Array.isArray(arr[d.index[0]])) {
1144-
val = arr[d.index[0]][d.index[1]];
1145-
}
1146-
} else {
1147-
val = Lib.nestedProperty(trace, traceKey).get();
1148-
}
1149-
1150-
if(val) d[key] = val;
1151-
}
1152-
1153-
fill('color', 'hbg', 'hoverlabel.bgcolor');
1154-
fill('borderColor', 'hbc', 'hoverlabel.bordercolor');
1155-
fill('fontFamily', 'htf', 'hoverlabel.font.family');
1156-
fill('fontSize', 'hts', 'hoverlabel.font.size');
1157-
fill('fontColor', 'htc', 'hoverlabel.font.color');
1158-
11591160
return d;
11601161
}
11611162

src/lib/coerce.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ exports.valObjects = {
196196
'Values in `extras` cannot be combined.'
197197
].join(' '),
198198
requiredOpts: ['flags'],
199-
otherOpts: ['dflt', 'extras'],
199+
otherOpts: ['dflt', 'extras', 'arrayOk'],
200200
coerceFunction: function(v, propOut, dflt, opts) {
201201
if(typeof v !== 'string') {
202202
propOut.set(dflt);

src/lib/index.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -349,10 +349,22 @@ lib.noneOrAll = function(containerIn, containerOut, attrList) {
349349
}
350350
};
351351

352-
lib.mergeArray = function(traceAttr, cd, cdAttr) {
352+
/** merge data array into calcdata items
353+
*
354+
* @param {array} traceAttr : trace attribute
355+
* @param {object} cd : calcdata trace
356+
* @param {string} cdAttr : calcdata key
357+
* @param {function} [fn] : optional function to apply to each array item
358+
*
359+
*/
360+
lib.mergeArray = function(traceAttr, cd, cdAttr, fn) {
361+
fn = fn || lib.identity;
362+
353363
if(Array.isArray(traceAttr)) {
354364
var imax = Math.min(traceAttr.length, cd.length);
355-
for(var i = 0; i < imax; i++) cd[i][cdAttr] = traceAttr[i];
365+
for(var i = 0; i < imax; i++) {
366+
cd[i][cdAttr] = fn(traceAttr[i]);
367+
}
356368
}
357369
};
358370

src/plot_api/helpers.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ exports.swapXYData = function(trace) {
415415
Lib.swapAttrs(trace, ['error_?.color', 'error_?.thickness', 'error_?.width']);
416416
}
417417
}
418-
if(trace.hoverinfo) {
418+
if(typeof trace.hoverinfo === 'string') {
419419
var hoverInfoParts = trace.hoverinfo.split('+');
420420
for(i = 0; i < hoverInfoParts.length; i++) {
421421
if(hoverInfoParts[i] === 'x') hoverInfoParts[i] = 'y';

src/plots/attributes.js

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ module.exports = {
7474
role: 'info',
7575
flags: ['x', 'y', 'z', 'text', 'name'],
7676
extras: ['all', 'none', 'skip'],
77+
arrayOk: true,
7778
dflt: 'all',
7879
description: [
7980
'Determines which trace information appear on hover.',

test/jasmine/tests/hover_label_test.js

+45-9
Original file line numberDiff line numberDiff line change
@@ -1034,14 +1034,18 @@ describe('Test hover label custom styling:', function() {
10341034
function assertLabel(className, expectation) {
10351035
var g = d3.select('g.' + className);
10361036

1037-
var path = g.select('path');
1038-
expect(path.style('fill')).toEqual(expectation.path[0], 'bgcolor');
1039-
expect(path.style('stroke')).toEqual(expectation.path[1], 'bordercolor');
1040-
1041-
var text = g.select({hovertext: 'text.nums', axistext: 'text'}[className]);
1042-
expect(parseInt(text.style('font-size'))).toEqual(expectation.text[0], 'font.size');
1043-
expect(text.style('font-family').split(',')[0]).toEqual(expectation.text[1], 'font.family');
1044-
expect(text.style('fill')).toEqual(expectation.text[2], 'font.color');
1037+
if(expectation === null) {
1038+
expect(g.size()).toBe(0);
1039+
} else {
1040+
var path = g.select('path');
1041+
expect(path.style('fill')).toEqual(expectation.path[0], 'bgcolor');
1042+
expect(path.style('stroke')).toEqual(expectation.path[1], 'bordercolor');
1043+
1044+
var text = g.select({hovertext: 'text.nums', axistext: 'text'}[className]);
1045+
expect(parseInt(text.style('font-size'))).toEqual(expectation.text[0], 'font.size');
1046+
expect(text.style('font-family').split(',')[0]).toEqual(expectation.text[1], 'font.family');
1047+
expect(text.style('fill')).toEqual(expectation.text[2], 'font.color');
1048+
}
10451049
}
10461050

10471051
function assertPtLabel(expectation) {
@@ -1112,8 +1116,40 @@ describe('Test hover label custom styling:', function() {
11121116
text: [13, 'Arial', 'rgb(255, 255, 255)']
11131117
});
11141118

1119+
// test arrayOk case
1120+
return Plotly.restyle(gd, 'hoverinfo', [['skip', 'name', 'x']]);
1121+
})
1122+
.then(function() {
1123+
_hover(gd, { xval: gd._fullData[0].x[0] });
1124+
1125+
assertPtLabel(null);
1126+
assertCommonLabel(null);
1127+
})
1128+
.then(function() {
1129+
_hover(gd, { xval: gd._fullData[0].x[1] });
1130+
1131+
assertPtLabel({
1132+
path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'],
1133+
text: [20, 'Arial', 'rgb(0, 128, 0)']
1134+
});
1135+
assertCommonLabel(null);
1136+
})
1137+
.then(function() {
1138+
_hover(gd, { xval: gd._fullData[0].x[2] });
1139+
1140+
assertPtLabel(null);
1141+
assertCommonLabel({
1142+
path: ['rgb(255, 255, 255)', 'rgb(255, 255, 255)'],
1143+
text: [13, 'Arial', 'rgb(255, 255, 255)']
1144+
});
1145+
11151146
// test base case
1116-
return Plotly.update(gd, { hoverlabel: null }, { hoverlabel: null });
1147+
return Plotly.update(gd, {
1148+
hoverlabel: null,
1149+
hoverinfo: null
1150+
}, {
1151+
hoverlabel: null
1152+
});
11171153
})
11181154
.then(function() {
11191155
_hover(gd, { xval: gd._fullData[0].x[0] });

0 commit comments

Comments
 (0)