diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js
index 0cb5df93b86..76ada0f47b0 100644
--- a/src/components/fx/hover.js
+++ b/src/components/fx/hover.js
@@ -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);
}
diff --git a/src/traces/box/attributes.js b/src/traces/box/attributes.js
index 3a8a1f3c4c8..66476cb1f26 100644
--- a/src/traces/box/attributes.js
+++ b/src/traces/box/attributes.js
@@ -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;
@@ -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,
diff --git a/src/traces/box/defaults.js b/src/traces/box/defaults.js
index e3d61a37b69..3bba43eb33d 100644
--- a/src/traces/box/defaults.js
+++ b/src/traces/box/defaults.js
@@ -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);
}
diff --git a/src/traces/box/hover.js b/src/traces/box/hover.js
index e2cc11969a6..c97d040db43 100644
--- a/src/traces/box/hover.js
+++ b/src/traces/box/hover.js
@@ -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);
}
@@ -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;
diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js
index d82509e1c98..c1c9410eb5e 100644
--- a/src/traces/scatter/hover.js
+++ b/src/traces/scatter/hover.js
@@ -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;
diff --git a/src/traces/violin/attributes.js b/src/traces/violin/attributes.js
index 2b7e08a713f..af5f76d44f1 100644
--- a/src/traces/violin/attributes.js
+++ b/src/traces/violin/attributes.js
@@ -147,6 +147,7 @@ module.exports = {
marker: boxAttrs.marker,
text: boxAttrs.text,
hovertext: boxAttrs.hovertext,
+ hovertemplate: boxAttrs.hovertemplate,
box: {
visible: {
diff --git a/src/traces/violin/hover.js b/src/traces/violin/hover.js
index cb70d91a63e..184e41744c7 100644
--- a/src/traces/violin/hover.js
+++ b/src/traces/violin/hover.js
@@ -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};
diff --git a/test/jasmine/tests/box_test.js b/test/jasmine/tests/box_test.js
index 527b540c806..86b2bc6e552 100644
--- a/test/jasmine/tests/box_test.js
+++ b/test/jasmine/tests/box_test.js
@@ -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'}],
@@ -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}pt #%{pointNumber}';
+ });
+ 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);
diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js
index 89792ed50ae..973a458e2c0 100644
--- a/test/jasmine/tests/hover_label_test.js
+++ b/test/jasmine/tests/hover_label_test.js
@@ -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() {
diff --git a/test/jasmine/tests/violin_test.js b/test/jasmine/tests/violin_test.js
index 231e26a19f9..80bfa26d8a5 100644
--- a/test/jasmine/tests/violin_test.js
+++ b/test/jasmine/tests/violin_test.js
@@ -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'}],
@@ -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}';
+ });
+ 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) {