Skip to content

Commit

Permalink
Merge pull request #3685 from plotly/box-violin-pts-hovertemplate
Browse files Browse the repository at this point in the history
Hovertemplate for box/violin points
  • Loading branch information
etpinard authored Mar 26, 2019
2 parents 682eb66 + ea34d6c commit 7b2946a
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 7 deletions.
12 changes: 8 additions & 4 deletions src/components/fx/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -672,11 +672,15 @@ function _hover(gd, evt, subplot, noHoverEvent) {
var pt = hoverData[itemnum];
var eventData = helpers.makeEventData(pt, pt.trace, pt.cd);

var ht = false;
if(pt.cd[pt.index] && pt.cd[pt.index].ht) ht = pt.cd[pt.index].ht;
hoverData[itemnum].hovertemplate = ht || pt.trace.hovertemplate || false;
hoverData[itemnum].eventData = [eventData];
if(pt.hovertemplate !== false) {
var ht = false;
if(pt.cd[pt.index] && pt.cd[pt.index].ht) {
ht = pt.cd[pt.index].ht;
}
pt.hovertemplate = ht || pt.trace.hovertemplate || false;
}

pt.eventData = [eventData];
newhoverdata.push(eventData);
}

Expand Down
6 changes: 6 additions & 0 deletions src/traces/box/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
var scatterAttrs = require('../scatter/attributes');
var barAttrs = require('../bar/attributes');
var colorAttrs = require('../../components/color/attributes');
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var extendFlat = require('../../lib/extend').extendFlat;

var scatterMarkerAttrs = scatterAttrs.marker;
Expand Down Expand Up @@ -76,6 +77,11 @@ module.exports = {
hovertext: extendFlat({}, scatterAttrs.hovertext, {
description: 'Same as `text`.'
}),
hovertemplate: hovertemplateAttrs({
description: [
'N.B. This only has an effect when hovering on points.'
].join(' ')
}),
whiskerwidth: {
valType: 'number',
min: 0,
Expand Down
5 changes: 4 additions & 1 deletion src/traces/box/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ function handlePointsDefaults(traceIn, traceOut, coerce, opts) {
delete traceOut.marker;
}

coerce('hoveron');
var hoveron = coerce('hoveron');
if(hoveron === 'all' || hoveron.indexOf('points') !== -1) {
coerce('hovertemplate');
}

Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
}
Expand Down
7 changes: 6 additions & 1 deletion src/traces/box/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,15 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) {
if(attr === 'mean' && ('sd' in di) && trace.boxmean === 'sd') {
pointData2[vLetter + 'err'] = di.sd;
}

// only keep name and spikes on the first item (median)
pointData.name = '';
pointData.spikeDistance = undefined;
pointData[spikePosAttr] = undefined;

// no hovertemplate support yet
pointData2.hovertemplate = false;

closeBoxData.push(pointData2);
}

Expand Down Expand Up @@ -242,7 +246,8 @@ function hoverOnPoints(pointData, xval, yval) {
x1: xc + rad,
y0: yc - rad,
y1: yc + rad,
spikeDistance: pointData.distance
spikeDistance: pointData.distance,
hovertemplate: trace.hovertemplate
});

var pa;
Expand Down
2 changes: 1 addition & 1 deletion src/traces/scatter/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
y0: yAvg,
y1: yAvg,
color: color,
hovertemplate: '%{name}'
hovertemplate: false
});

delete pointData.index;
Expand Down
1 change: 1 addition & 0 deletions src/traces/violin/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ module.exports = {
marker: boxAttrs.marker,
text: boxAttrs.text,
hovertext: boxAttrs.hovertext,
hovertemplate: boxAttrs.hovertemplate,

box: {
visible: {
Expand Down
3 changes: 3 additions & 0 deletions src/traces/violin/hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay
closeBoxData[0].spikeDistance = undefined;
closeBoxData[0][spikePosAttr] = undefined;

// no hovertemplate support yet
kdePointData.hovertemplate = false;

closeData.push(kdePointData);

violinLineAttrs = {stroke: pointData.color};
Expand Down
47 changes: 47 additions & 0 deletions test/jasmine/tests/box_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,40 @@ describe('Test boxes supplyDefaults', function() {
expect(traceOut.text).toBeDefined();
});

describe('should not coerce hovertemplate when *hoveron* does not contains *points* flag', function() {
var ht = '--- %{y}';

it('- case hoveron:points', function() {
traceIn = {
y: [1, 1, 2],
hoveron: 'points',
hovertemplate: ht
};
supplyDefaults(traceIn, traceOut, defaultColor, {});
expect(traceOut.hovertemplate).toBe(ht);
});

it('- case hoveron:points+boxes', function() {
traceIn = {
y: [1, 1, 2],
hoveron: 'points+boxes',
hovertemplate: ht
};
supplyDefaults(traceIn, traceOut, defaultColor, {});
expect(traceOut.hovertemplate).toBe(ht);
});

it('- case hoveron:boxes', function() {
traceIn = {
y: [1, 1, 2],
hoveron: 'boxes',
hovertemplate: ht
};
supplyDefaults(traceIn, traceOut, defaultColor, {});
expect(traceOut.hovertemplate).toBe(undefined);
});
});

it('should not include alignementgroup/offsetgroup when boxmode is not *group*', function() {
var gd = {
data: [{type: 'box', y: [1], alignmentgroup: 'a', offsetgroup: '1'}],
Expand Down Expand Up @@ -391,6 +425,19 @@ describe('Test box hover:', function() {
pos: [202, 335],
nums: '(2, 13.1)',
name: ''
}, {
desc: 'with hovertemplate for points',
patch: function(fig) {
fig.data.forEach(function(trace) {
trace.boxpoints = 'all';
trace.hoveron = 'points';
trace.hovertemplate = '%{y}<extra>pt #%{pointNumber}</extra>';
});
fig.layout.hovermode = 'closest';
return fig;
},
nums: '0.6',
name: 'pt #0'
}].forEach(function(specs) {
it('should generate correct hover labels ' + specs.desc, function(done) {
run(specs).catch(failTest).then(done);
Expand Down
32 changes: 32 additions & 0 deletions test/jasmine/tests/hover_label_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1938,6 +1938,38 @@ describe('hover info', function() {
.catch(failTest)
.then(done);
});

it('should fallback to regular hover content when hoveron does not support hovertemplate', function(done) {
var gd = createGraphDiv();
var fig = Lib.extendDeep({}, require('@mocks/scatter_fill_self_next.json'));

fig.data.forEach(function(trace) {
trace.hoveron = 'points+fills';
trace.hovertemplate = '%{x} | %{y}';
});

fig.layout.hovermode = 'closest';
fig.layout.showlegend = false;
fig.layout.margin = {t: 0, b: 0, l: 0, r: 0};

Plotly.plot(gd, fig)
.then(function() { _hoverNatural(gd, 180, 200); })
.then(function() {
assertHoverLabelContent({
nums: 'trace 1',
name: ''
}, 'hovering on a fill');
})
.then(function() { _hoverNatural(gd, 50, 95); })
.then(function() {
assertHoverLabelContent({
nums: '0 | 5',
name: 'trace 1'
}, 'hovering on a pt');
})
.catch(failTest)
.then(done);
});
});

describe('hover info on stacked subplots', function() {
Expand Down
46 changes: 46 additions & 0 deletions test/jasmine/tests/violin_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,38 @@ describe('Test violin defaults', function() {
expect(traceOut.scalegroup).toBe('');
});

it('should not coerce hovertemplate when *hoveron* does not contains *points* flag', function() {
var ht = '--- %{y} ---';

_supply({
y: [1, 2, 1],
hoveron: 'points',
hovertemplate: ht
});
expect(traceOut.hovertemplate).toBe(ht, 'hoveron:points');

_supply({
y: [1, 2, 1],
hoveron: 'kde',
hovertemplate: ht
});
expect(traceOut.hovertemplate).toBe(undefined, 'hoveron:kde');

_supply({
y: [1, 2, 1],
hoveron: 'all',
hovertemplate: ht
});
expect(traceOut.hovertemplate).toBe(ht, 'hoveron:all');

_supply({
y: [1, 2, 1],
hoveron: 'violins+points',
hovertemplate: ht
});
expect(traceOut.hovertemplate).toBe(ht, 'hoveron:violins+points');
});

it('should not include alignementgroup/offsetgroup when violinmode is not *group*', function() {
var gd = {
data: [{type: 'violin', y: [1], alignmentgroup: 'a', offsetgroup: '1'}],
Expand Down Expand Up @@ -591,6 +623,20 @@ describe('Test violin hover:', function() {
pos: [417, 309],
nums: '(14, 2)',
name: ''
}, {
desc: 'with hovertemplate for points',
patch: function(fig) {
fig.data.forEach(function(trace) {
trace.points = 'all';
trace.hoveron = 'points';
trace.hovertemplate = 'Sample pt %{pointNumber}: %{y:.3f}<extra></extra>';
});
fig.layout.hovermode = 'closest';
return fig;
},
pos: [220, 200],
nums: 'Sample pt 3: 0.900',
name: ''
}]
.forEach(function(specs) {
it('should generate correct hover labels ' + specs.desc, function(done) {
Expand Down

0 comments on commit 7b2946a

Please sign in to comment.