Skip to content

Commit e8a9ddd

Browse files
committed
add arrayOk support for hoverinfo for gl3d, gl2d and pie traces
- To do so, add Fx.castHoverinfo helper and use it during gl3d, gl2d and pie hover where hoverinfo (trace-wide and per-pt array) is extracted from fullData
1 parent 371d31e commit e8a9ddd

File tree

9 files changed

+108
-22
lines changed

9 files changed

+108
-22
lines changed

src/components/fx/index.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ module.exports = {
3535
getDistanceFunction: helpers.getDistanceFunction,
3636
getClosest: helpers.getClosest,
3737
inbox: helpers.inbox,
38+
3839
castHoverOption: castHoverOption,
40+
castHoverinfo: castHoverinfo,
3941

4042
hover: require('./hover').hover,
4143
unhover: dragElement.unhover,
@@ -57,18 +59,16 @@ function loneUnhover(containerOrSelection) {
5759
selection.selectAll('.spikeline').remove();
5860
}
5961

60-
// Handler for trace-wide vs per-point hover label options
62+
// helpers for traces that use Fx.loneHover
63+
6164
function castHoverOption(trace, ptNumber, attr) {
62-
var labelOpts = trace.hoverlabel || {};
63-
var val = Lib.nestedProperty(labelOpts, attr).get();
64-
65-
if(Array.isArray(val)) {
66-
if(Array.isArray(ptNumber) && Array.isArray(val[ptNumber[0]])) {
67-
return val[ptNumber[0]][ptNumber[1]];
68-
} else {
69-
return val[ptNumber];
70-
}
71-
} else {
72-
return val;
65+
return Lib.castOption(trace, ptNumber, 'hoverlabel.' + attr);
66+
}
67+
68+
function castHoverinfo(trace, fullLayout, ptNumber) {
69+
function _coerce(val) {
70+
return Lib.coerceHoverinfo({hoverinfo: val}, {_module: trace._module}, fullLayout);
7371
}
72+
73+
return Lib.castOption(trace, ptNumber, 'hoverinfo', _coerce);
7474
}

src/lib/index.js

+25
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,31 @@ lib.mergeArray = function(traceAttr, cd, cdAttr, fn) {
368368
}
369369
};
370370

371+
/** Handler for trace-wide vs per-point options
372+
*
373+
* @param {object} trace : (full) trace object
374+
* @param {number} ptNumber : index of the point in question
375+
* @param {string} astr : attribute string
376+
* @param {function} [fn] : optional function to apply to each array item
377+
*
378+
* @return {any}
379+
*/
380+
lib.castOption = function(trace, ptNumber, astr, fn) {
381+
fn = fn || lib.identity;
382+
383+
var val = lib.nestedProperty(trace, astr).get();
384+
385+
if(Array.isArray(val)) {
386+
if(Array.isArray(ptNumber) && Array.isArray(val[ptNumber[0]])) {
387+
return fn(val[ptNumber[0]][ptNumber[1]]);
388+
} else {
389+
return fn(val[ptNumber]);
390+
}
391+
} else {
392+
return val;
393+
}
394+
};
395+
371396
/** Returns target as set by 'target' transform attribute
372397
*
373398
* @param {object} trace : full trace object

src/plots/gl2d/scene2d.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -623,8 +623,11 @@ proto.draw = function() {
623623
// also it's important to copy, otherwise data is lost by the time event data is read
624624
this.emitPointAction(nextSelection, 'plotly_hover');
625625

626-
var hoverinfo = selection.hoverinfo;
627-
if(hoverinfo !== 'all') {
626+
var trace = this.fullData[selection.trace.index] || {};
627+
var ptNumber = selection.pointIndex;
628+
var hoverinfo = Fx.castHoverinfo(trace, fullLayout, ptNumber);
629+
630+
if(hoverinfo && hoverinfo !== 'all') {
628631
var parts = hoverinfo.split('+');
629632
if(parts.indexOf('x') === -1) selection.traceCoord[0] = undefined;
630633
if(parts.indexOf('y') === -1) selection.traceCoord[1] = undefined;
@@ -633,9 +636,6 @@ proto.draw = function() {
633636
if(parts.indexOf('name') === -1) selection.name = undefined;
634637
}
635638

636-
var trace = this.fullData[selection.trace.index] || {};
637-
var ptNumber = selection.pointIndex;
638-
639639
Fx.loneHover({
640640
x: selection.screenCoord[0],
641641
y: selection.screenCoord[1],

src/plots/gl3d/scene.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ function render(scene) {
6767
if(lastPicked !== null) {
6868
var pdata = project(scene.glplot.cameraParams, selection.dataCoordinate);
6969
trace = lastPicked.data;
70-
var hoverinfo = trace.hoverinfo;
7170
var ptNumber = selection.index;
71+
var hoverinfo = Fx.castHoverinfo(trace, scene.fullLayout, ptNumber);
7272

7373
var xVal = formatter('xaxis', selection.traceCoordinate[0]),
7474
yVal = formatter('yaxis', selection.traceCoordinate[1]),

src/traces/pie/plot.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,9 @@ module.exports = function plot(gd, cdpie) {
8989
evt.originalEvent = d3.event;
9090

9191
// in case fullLayout or fullData has changed without a replot
92-
var fullLayout2 = gd._fullLayout,
93-
trace2 = gd._fullData[trace.index],
94-
hoverinfo = trace2.hoverinfo;
92+
var fullLayout2 = gd._fullLayout;
93+
var trace2 = gd._fullData[trace.index];
94+
var hoverinfo = Fx.castHoverinfo(trace2, fullLayout2, pt.i);
9595

9696
if(hoverinfo === 'all') hoverinfo = 'label+text+value+percent+name';
9797

test/jasmine/tests/gl2d_click_test.js

+22
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,25 @@ describe('Test hover and click interactions', function() {
145145
expect(text.style('fill')).toEqual(expected.fontColor, 'font.color');
146146
}
147147

148+
function assertHoveLabelContent(expected) {
149+
var label = expected.label;
150+
151+
if(label === undefined) return;
152+
153+
var g = d3.select('.hovertext');
154+
155+
if(label === null) {
156+
expect(g.size()).toBe(0);
157+
} else {
158+
var lines = g.selectAll('text.nums');
159+
160+
expect(lines.size()).toBe(label.length);
161+
lines.each(function(_, i) {
162+
expect(d3.select(this).text()).toEqual(label[i]);
163+
});
164+
}
165+
}
166+
148167
// returns basic hover/click/unhover runner for one xy position
149168
function makeRunner(pos, expected, opts) {
150169
opts = opts || {};
@@ -162,6 +181,7 @@ describe('Test hover and click interactions', function() {
162181
.then(function(eventData) {
163182
assertEventData(eventData, expected);
164183
assertHoverLabelStyle(d3.select('g.hovertext'), expected);
184+
assertHoveLabelContent(expected);
165185
})
166186
.then(_click)
167187
.then(function(eventData) {
@@ -196,6 +216,7 @@ describe('Test hover and click interactions', function() {
196216
color: 'yellow'
197217
}
198218
};
219+
_mock.data[0].hoverinfo = _mock.data[0].x.map(function(_, i) { return i % 2 ? 'y' : 'x'; });
199220
_mock.data[0].hoverlabel = {
200221
bgcolor: 'blue',
201222
bordercolor: _mock.data[0].x.map(function(_, i) { return i % 2 ? 'red' : 'green'; })
@@ -204,6 +225,7 @@ describe('Test hover and click interactions', function() {
204225
var run = makeRunner([655, 317], {
205226
x: 15.772,
206227
y: 0.387,
228+
label: ['0.387'],
207229
curveNumber: 0,
208230
pointNumber: 33,
209231
bgColor: 'rgb(0, 0, 255)',

test/jasmine/tests/gl_plot_interact_test.js

+26
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,23 @@ describe('Test gl3d plots', function() {
180180
.then(_hover)
181181
.then(function() {
182182
assertHoverLabelStyle('rgb(0, 128, 0)', 'rgb(255, 255, 0)', 20, 'Roboto', 'rgb(0, 255, 255)');
183+
184+
return Plotly.restyle(gd, 'hoverinfo', [[null, null, 'y', null]]);
183185
})
186+
.then(_hover)
187+
.then(function() {
188+
var label = d3.selectAll('g.hovertext');
189+
190+
expect(label.size()).toEqual(1);
191+
expect(label.select('text').text()).toEqual('c');
192+
193+
return Plotly.restyle(gd, 'hoverinfo', [[null, null, 'dont+know', null]]);
194+
})
195+
.then(_hover)
196+
.then(function() {
197+
assertHoverText('x: 二 6, 2017', 'y: c', 'z: 100k', 'Clementine');
198+
})
199+
.catch(fail)
184200
.then(done);
185201
});
186202

@@ -207,6 +223,11 @@ describe('Test gl3d plots', function() {
207223
assertHoverLabelStyle('rgb(68, 68, 68)', 'rgb(255, 255, 255)', 13, 'Arial', 'rgb(255, 255, 255)');
208224

209225
Plotly.restyle(gd, {
226+
'hoverinfo': [[
227+
['all', 'all', 'all'],
228+
['all', 'all', 'y'],
229+
['all', 'all', 'all']
230+
]],
210231
'hoverlabel.bgcolor': 'white',
211232
'hoverlabel.font.size': 9,
212233
'hoverlabel.font.color': [[
@@ -219,6 +240,11 @@ describe('Test gl3d plots', function() {
219240
.then(_hover)
220241
.then(function() {
221242
assertHoverLabelStyle('rgb(255, 255, 255)', 'rgb(68, 68, 68)', 9, 'Arial', 'rgb(0, 255, 255)');
243+
244+
var label = d3.selectAll('g.hovertext');
245+
246+
expect(label.size()).toEqual(1);
247+
expect(label.select('text').text()).toEqual('2');
222248
})
223249
.then(done);
224250
});

test/jasmine/tests/hover_label_test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1146,7 +1146,8 @@ describe('Test hover label custom styling:', function() {
11461146
// test base case
11471147
return Plotly.update(gd, {
11481148
hoverlabel: null,
1149-
hoverinfo: null
1149+
// all these items should be display as 'all'
1150+
hoverinfo: [['i+dont+what+im+doing', null, undefined]]
11501151
}, {
11511152
hoverlabel: null
11521153
});

test/jasmine/tests/hover_pie_test.js

+12
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,18 @@ describe('pie hovering', function() {
255255
['4', 'SUP', '5', '33.3%'],
256256
['rgb(255, 0, 0)', 'rgb(255, 255, 0)', 15, 'Roboto', 'rgb(0, 0, 255)']
257257
);
258+
259+
return Plotly.restyle(gd, 'hoverinfo', [[null, null, null, null, 'label+percent']]);
260+
})
261+
.then(_hover)
262+
.then(function() {
263+
assertLabel(['4', '33.3%']);
264+
265+
return Plotly.restyle(gd, 'hoverinfo', [[null, null, null, null, 'dont+know+what+im-doing']]);
266+
})
267+
.then(_hover)
268+
.then(function() {
269+
assertLabel(['4', 'SUP', '5', '33.3%']);
258270
})
259271
.catch(fail)
260272
.then(done);

0 commit comments

Comments
 (0)