diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js
index 9c15427cd1c..337a657cf9b 100644
--- a/src/components/fx/hover.js
+++ b/src/components/fx/hover.js
@@ -940,7 +940,7 @@ function createHoverText(hoverData, opts, gd) {
text = d[(hovermode === 'x' ? 'y' : 'x') + 'Label'] || '';
}
else if(d.xLabel === undefined) {
- if(d.yLabel !== undefined) text = d.yLabel;
+ if(d.yLabel !== undefined && d.trace.type !== 'scattercarpet') text = d.yLabel;
}
else if(d.yLabel === undefined) text = d.xLabel;
else text = '(' + d.xLabel + ', ' + d.yLabel + ')';
diff --git a/src/components/fx/hovertemplate_attributes.js b/src/components/fx/hovertemplate_attributes.js
index 8c5f8003464..094af37dad1 100644
--- a/src/components/fx/hovertemplate_attributes.js
+++ b/src/components/fx/hovertemplate_attributes.js
@@ -31,8 +31,7 @@ module.exports = function(opts, extra) {
valType: 'string',
role: 'info',
dflt: '',
- arrayOk: true,
- editType: 'none',
+ editType: opts.editType || 'none',
description: [
'Template string used for rendering the information that appear on hover box.',
'Note that this will override `hoverinfo`.',
@@ -46,5 +45,9 @@ module.exports = function(opts, extra) {
].join(' ')
};
+ if(opts.arrayOk !== false) {
+ hovertemplate.arrayOk = true;
+ }
+
return hovertemplate;
};
diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js
index 5d3ebe23297..72c526f937b 100644
--- a/src/plots/gl3d/scene.js
+++ b/src/plots/gl3d/scene.js
@@ -71,18 +71,21 @@ function render(scene) {
var pdata = project(scene.glplot.cameraParams, selection.dataCoordinate);
trace = lastPicked.data;
var ptNumber = selection.index;
- var hoverinfo = Fx.castHoverinfo(trace, scene.fullLayout, ptNumber);
- var hoverinfoParts = hoverinfo.split('+');
- var isHoverinfoAll = hoverinfo === 'all';
- var xVal = formatter('xaxis', selection.traceCoordinate[0]);
- var yVal = formatter('yaxis', selection.traceCoordinate[1]);
- var zVal = formatter('zaxis', selection.traceCoordinate[2]);
+ var labels = {
+ xLabel: formatter('xaxis', selection.traceCoordinate[0]),
+ yLabel: formatter('yaxis', selection.traceCoordinate[1]),
+ zLabel: formatter('zaxis', selection.traceCoordinate[2])
+ };
+
+ var hoverinfo = Fx.castHoverinfo(trace, scene.fullLayout, ptNumber);
+ var hoverinfoParts = (hoverinfo || '').split('+');
+ var isHoverinfoAll = hoverinfo && hoverinfo === 'all';
- if(!isHoverinfoAll) {
- if(hoverinfoParts.indexOf('x') === -1) xVal = undefined;
- if(hoverinfoParts.indexOf('y') === -1) yVal = undefined;
- if(hoverinfoParts.indexOf('z') === -1) zVal = undefined;
+ if(!trace.hovertemplate && !isHoverinfoAll) {
+ if(hoverinfoParts.indexOf('x') === -1) labels.xLabel = undefined;
+ if(hoverinfoParts.indexOf('y') === -1) labels.yLabel = undefined;
+ if(hoverinfoParts.indexOf('z') === -1) labels.zLabel = undefined;
if(hoverinfoParts.indexOf('text') === -1) selection.textLabel = undefined;
if(hoverinfoParts.indexOf('name') === -1) lastPicked.name = undefined;
}
@@ -91,27 +94,38 @@ function render(scene) {
var vectorTx = [];
if(trace.type === 'cone' || trace.type === 'streamtube') {
+ labels.uLabel = formatter('xaxis', selection.traceCoordinate[3]);
if(isHoverinfoAll || hoverinfoParts.indexOf('u') !== -1) {
- vectorTx.push('u: ' + formatter('xaxis', selection.traceCoordinate[3]));
+ vectorTx.push('u: ' + labels.uLabel);
}
+
+ labels.vLabel = formatter('yaxis', selection.traceCoordinate[4]);
if(isHoverinfoAll || hoverinfoParts.indexOf('v') !== -1) {
- vectorTx.push('v: ' + formatter('yaxis', selection.traceCoordinate[4]));
+ vectorTx.push('v: ' + labels.vLabel);
}
+
+ labels.wLabel = formatter('zaxis', selection.traceCoordinate[5]);
if(isHoverinfoAll || hoverinfoParts.indexOf('w') !== -1) {
- vectorTx.push('w: ' + formatter('zaxis', selection.traceCoordinate[5]));
+ vectorTx.push('w: ' + labels.wLabel);
}
+
+ labels.normLabel = selection.traceCoordinate[6].toPrecision(3);
if(isHoverinfoAll || hoverinfoParts.indexOf('norm') !== -1) {
- vectorTx.push('norm: ' + selection.traceCoordinate[6].toPrecision(3));
+ vectorTx.push('norm: ' + labels.normLabel);
}
- if(trace.type === 'streamtube' && (isHoverinfoAll || hoverinfoParts.indexOf('divergence') !== -1)) {
- vectorTx.push('divergence: ' + selection.traceCoordinate[7].toPrecision(3));
+ if(trace.type === 'streamtube') {
+ labels.divergenceLabel = selection.traceCoordinate[7].toPrecision(3);
+ if(isHoverinfoAll || hoverinfoParts.indexOf('divergence') !== -1) {
+ vectorTx.push('divergence: ' + labels.divergenceLabel);
+ }
}
if(selection.textLabel) {
vectorTx.push(selection.textLabel);
}
tx = vectorTx.join('
');
} else if(trace.type === 'isosurface') {
- vectorTx.push('value: ' + Axes.tickText(scene.mockAxis, scene.mockAxis.d2l(selection.traceCoordinate[3]), 'hover').text);
+ labels.valueLabel = Axes.tickText(scene.mockAxis, scene.mockAxis.d2l(selection.traceCoordinate[3]), 'hover').text;
+ vectorTx.push('value: ' + labels.valueLabel);
if(selection.textLabel) {
vectorTx.push(selection.textLabel);
}
@@ -120,27 +134,6 @@ function render(scene) {
tx = selection.textLabel;
}
- if(scene.fullSceneLayout.hovermode) {
- Fx.loneHover({
- x: (0.5 + 0.5 * pdata[0] / pdata[3]) * width,
- y: (0.5 - 0.5 * pdata[1] / pdata[3]) * height,
- xLabel: xVal,
- yLabel: yVal,
- zLabel: zVal,
- text: tx,
- name: lastPicked.name,
- color: Fx.castHoverOption(trace, ptNumber, 'bgcolor') || lastPicked.color,
- borderColor: Fx.castHoverOption(trace, ptNumber, 'bordercolor'),
- fontFamily: Fx.castHoverOption(trace, ptNumber, 'font.family'),
- fontSize: Fx.castHoverOption(trace, ptNumber, 'font.size'),
- fontColor: Fx.castHoverOption(trace, ptNumber, 'font.color')
- }, {
- container: svgContainer,
- gd: scene.graphDiv
- });
- }
-
- // TODO not sure if streamtube x/y/z should be emitted as x/y/z
var pointData = {
x: selection.traceCoordinate[0],
y: selection.traceCoordinate[1],
@@ -151,18 +144,41 @@ function render(scene) {
pointNumber: ptNumber
};
+ Fx.appendArrayPointValue(pointData, trace, ptNumber);
+
if(trace._module.eventData) {
pointData = trace._module.eventData(pointData, selection, trace, {}, ptNumber);
}
- Fx.appendArrayPointValue(pointData, trace, ptNumber);
-
var eventData = {points: [pointData]};
+ if(scene.fullSceneLayout.hovermode) {
+ Fx.loneHover({
+ trace: trace,
+ x: (0.5 + 0.5 * pdata[0] / pdata[3]) * width,
+ y: (0.5 - 0.5 * pdata[1] / pdata[3]) * height,
+ xLabel: labels.xLabel,
+ yLabel: labels.yLabel,
+ zLabel: labels.zLabel,
+ text: tx,
+ name: lastPicked.name,
+ color: Fx.castHoverOption(trace, ptNumber, 'bgcolor') || lastPicked.color,
+ borderColor: Fx.castHoverOption(trace, ptNumber, 'bordercolor'),
+ fontFamily: Fx.castHoverOption(trace, ptNumber, 'font.family'),
+ fontSize: Fx.castHoverOption(trace, ptNumber, 'font.size'),
+ fontColor: Fx.castHoverOption(trace, ptNumber, 'font.color'),
+ hovertemplate: Lib.castOption(trace, ptNumber, 'hovertemplate'),
+ hovertemplateLabels: Lib.extendFlat({}, pointData, labels),
+ eventData: [pointData]
+ }, {
+ container: svgContainer,
+ gd: scene.graphDiv
+ });
+ }
+
if(selection.buttons && selection.distance < 5) {
scene.graphDiv.emit('plotly_click', eventData);
- }
- else {
+ } else {
scene.graphDiv.emit('plotly_hover', eventData);
}
diff --git a/src/traces/cone/attributes.js b/src/traces/cone/attributes.js
index 0acbae90447..e2a2f062fe2 100644
--- a/src/traces/cone/attributes.js
+++ b/src/traces/cone/attributes.js
@@ -10,6 +10,7 @@
var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var mesh3dAttrs = require('../mesh3d/attributes');
var baseAttrs = require('../../plots/attributes');
@@ -157,7 +158,8 @@ var attrs = {
'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,',
'these elements will be seen in the hover labels.'
].join(' ')
- }
+ },
+ hovertemplate: hovertemplateAttrs({editType: 'calc'}, {keys: ['norm']})
};
extendFlat(attrs, colorscaleAttrs('', {
diff --git a/src/traces/cone/defaults.js b/src/traces/cone/defaults.js
index 6a5ec9e1fcc..7237edba166 100644
--- a/src/traces/cone/defaults.js
+++ b/src/traces/cone/defaults.js
@@ -52,6 +52,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'c'});
coerce('text');
+ coerce('hovertemplate');
// disable 1D transforms (for now)
traceOut._length = null;
diff --git a/src/traces/cone/index.js b/src/traces/cone/index.js
index d08a37e5200..e786cb9d47e 100644
--- a/src/traces/cone/index.js
+++ b/src/traces/cone/index.js
@@ -22,6 +22,10 @@ module.exports = {
},
calc: require('./calc'),
plot: require('./convert'),
+ eventData: function(out, pt) {
+ out.norm = pt.traceCoordinate[6];
+ return out;
+ },
meta: {
description: [
diff --git a/src/traces/contour/attributes.js b/src/traces/contour/attributes.js
index 9edef92e3b6..82055b84201 100644
--- a/src/traces/contour/attributes.js
+++ b/src/traces/contour/attributes.js
@@ -35,6 +35,7 @@ module.exports = extendFlat({
xtype: heatmapAttrs.xtype,
ytype: heatmapAttrs.ytype,
zhoverformat: heatmapAttrs.zhoverformat,
+ hovertemplate: heatmapAttrs.hovertemplate,
connectgaps: heatmapAttrs.connectgaps,
diff --git a/src/traces/contour/defaults.js b/src/traces/contour/defaults.js
index 650205e17be..aa6cb7c8842 100644
--- a/src/traces/contour/defaults.js
+++ b/src/traces/contour/defaults.js
@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
-
'use strict';
var Lib = require('../../lib');
@@ -34,6 +33,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
}
coerce('text');
+ coerce('hovertemplate');
+
var isConstraint = (coerce('contours.type') === 'constraint');
coerce('connectgaps', Lib.isArray1D(traceOut.z));
diff --git a/src/traces/heatmap/attributes.js b/src/traces/heatmap/attributes.js
index ce13169b456..9bf083d8098 100644
--- a/src/traces/heatmap/attributes.js
+++ b/src/traces/heatmap/attributes.js
@@ -9,6 +9,7 @@
'use strict';
var scatterAttrs = require('../scatter/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
@@ -111,6 +112,8 @@ module.exports = extendFlat({
'https://github.com/d3/d3-format/blob/master/README.md#locale_format'
].join(' ')
},
+ hovertemplate: hovertemplateAttrs()
+}, {
transforms: undefined
},
colorscaleAttrs('', {
diff --git a/src/traces/heatmap/defaults.js b/src/traces/heatmap/defaults.js
index 80e34a22fbc..6e2933cbc91 100644
--- a/src/traces/heatmap/defaults.js
+++ b/src/traces/heatmap/defaults.js
@@ -29,6 +29,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
}
coerce('text');
+ coerce('hovertemplate');
handleStyleDefaults(traceIn, traceOut, coerce, layout);
diff --git a/src/traces/histogram/event_data.js b/src/traces/histogram/event_data.js
index c46921426e3..ab5ac4f41a3 100644
--- a/src/traces/histogram/event_data.js
+++ b/src/traces/histogram/event_data.js
@@ -13,6 +13,9 @@ module.exports = function eventData(out, pt, trace, cd, pointNumber) {
out.x = 'xVal' in pt ? pt.xVal : pt.x;
out.y = 'yVal' in pt ? pt.yVal : pt.y;
+ // for 2d histograms
+ if('zLabelVal' in pt) out.z = pt.zLabelVal;
+
if(pt.xa) out.xaxis = pt.xa;
if(pt.ya) out.yaxis = pt.ya;
diff --git a/src/traces/histogram2d/attributes.js b/src/traces/histogram2d/attributes.js
index 34d8fb47ee7..72befadf1fd 100644
--- a/src/traces/histogram2d/attributes.js
+++ b/src/traces/histogram2d/attributes.js
@@ -11,6 +11,7 @@
var histogramAttrs = require('../histogram/attributes');
var makeBinAttrs = require('../histogram/bin_attributes');
var heatmapAttrs = require('../heatmap/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
@@ -47,7 +48,8 @@ module.exports = extendFlat(
xgap: heatmapAttrs.xgap,
ygap: heatmapAttrs.ygap,
zsmooth: heatmapAttrs.zsmooth,
- zhoverformat: heatmapAttrs.zhoverformat
+ zhoverformat: heatmapAttrs.zhoverformat,
+ hovertemplate: hovertemplateAttrs({}, {keys: 'z'})
},
colorscaleAttrs('', {
cLetter: 'z',
diff --git a/src/traces/histogram2d/defaults.js b/src/traces/histogram2d/defaults.js
index 8483915ea2e..3f0ea3f206c 100644
--- a/src/traces/histogram2d/defaults.js
+++ b/src/traces/histogram2d/defaults.js
@@ -29,4 +29,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
colorscaleDefaults(
traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}
);
+ coerce('hovertemplate');
};
diff --git a/src/traces/histogram2dcontour/attributes.js b/src/traces/histogram2dcontour/attributes.js
index e89b969fdf7..2dfe6dc89c2 100644
--- a/src/traces/histogram2dcontour/attributes.js
+++ b/src/traces/histogram2dcontour/attributes.js
@@ -34,7 +34,8 @@ module.exports = extendFlat({
ncontours: contourAttrs.ncontours,
contours: contourAttrs.contours,
line: contourAttrs.line,
- zhoverformat: histogram2dAttrs.zhoverformat
+ zhoverformat: histogram2dAttrs.zhoverformat,
+ hovertemplate: histogram2dAttrs.hovertemplate
},
colorscaleAttrs('', {
cLetter: 'z',
diff --git a/src/traces/histogram2dcontour/defaults.js b/src/traces/histogram2dcontour/defaults.js
index b8e40231075..a4792f149f7 100644
--- a/src/traces/histogram2dcontour/defaults.js
+++ b/src/traces/histogram2dcontour/defaults.js
@@ -31,4 +31,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
handleContoursDefaults(traceIn, traceOut, coerce, coerce2);
handleStyleDefaults(traceIn, traceOut, coerce, layout);
+ coerce('hovertemplate');
};
diff --git a/src/traces/isosurface/attributes.js b/src/traces/isosurface/attributes.js
index 031c89e58ff..c53ecf71918 100644
--- a/src/traces/isosurface/attributes.js
+++ b/src/traces/isosurface/attributes.js
@@ -10,6 +10,7 @@
var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var meshAttrs = require('../mesh3d/attributes');
var baseAttrs = require('../../plots/attributes');
@@ -224,7 +225,8 @@ var attrs = module.exports = overrideAll(extendFlat({
'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,',
'these elements will be seen in the hover labels.'
].join(' ')
- }
+ },
+ hovertemplate: hovertemplateAttrs()
},
colorscaleAttrs('', {
diff --git a/src/traces/isosurface/defaults.js b/src/traces/isosurface/defaults.js
index 23dce6b8290..f00502111bf 100644
--- a/src/traces/isosurface/defaults.js
+++ b/src/traces/isosurface/defaults.js
@@ -6,7 +6,6 @@
* LICENSE file in the root directory of this source tree.
*/
-
'use strict';
var Registry = require('../../registry');
@@ -85,6 +84,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
// Coerce remaining properties
[
'text',
+ 'hovertemplate',
'lighting.ambient',
'lighting.diffuse',
'lighting.specular',
diff --git a/src/traces/mesh3d/attributes.js b/src/traces/mesh3d/attributes.js
index 9a6f642475a..dea5596c3c0 100644
--- a/src/traces/mesh3d/attributes.js
+++ b/src/traces/mesh3d/attributes.js
@@ -10,6 +10,7 @@
var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var surfaceAtts = require('../surface/attributes');
var baseAttrs = require('../../plots/attributes');
@@ -89,6 +90,7 @@ module.exports = extendFlat({
'these elements will be seen in the hover labels.'
].join(' ')
},
+ hovertemplate: hovertemplateAttrs({editType: 'calc'}),
delaunayaxis: {
valType: 'enumerated',
diff --git a/src/traces/mesh3d/defaults.js b/src/traces/mesh3d/defaults.js
index 952f8cd73c5..c46204a11c2 100644
--- a/src/traces/mesh3d/defaults.js
+++ b/src/traces/mesh3d/defaults.js
@@ -88,6 +88,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
}
coerce('text');
+ coerce('hovertemplate');
// disable 1D transforms
// x/y/z should match lengths, and i/j/k should match as well, but
diff --git a/src/traces/parcats/attributes.js b/src/traces/parcats/attributes.js
index 0e06784df39..98b6ada66cd 100644
--- a/src/traces/parcats/attributes.js
+++ b/src/traces/parcats/attributes.js
@@ -12,6 +12,7 @@ var extendFlat = require('../../lib/extend').extendFlat;
var plotAttrs = require('../../plots/attributes');
var fontAttrs = require('../../plots/font_attributes');
var colorAttributes = require('../../components/colorscale/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var domainAttrs = require('../../plots/domain').attributes;
var scatterAttrs = require('../scatter/attributes');
var scatterLineAttrs = scatterAttrs.line;
@@ -34,16 +35,26 @@ var line = extendFlat({
'If `linear`, paths are composed of straight lines.',
'If `hspline`, paths are composed of horizontal curved splines'
].join(' ')
- }
+ },
+
+ hovertemplate: hovertemplateAttrs({
+ editType: 'plot',
+ arrayOk: false
+ }, {
+ keys: ['count', 'probability'],
+ description: [
+ 'This value here applies when hovering over lines.'
+ ].join(' ')
+ })
});
module.exports = {
domain: domainAttrs({name: 'parcats', trace: true, editType: 'calc'}),
+
hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {
flags: ['count', 'probability'],
editType: 'plot',
arrayOk: false
- // plotAttrs.hoverinfo description is appropriate
}),
hoveron: {
valType: 'enumerated',
@@ -58,6 +69,21 @@ module.exports = {
'If `dimension`, hover interactions take place across all categories per dimension.'
].join(' ')
},
+ hovertemplate: hovertemplateAttrs({
+ editType: 'plot',
+ arrayOk: false
+ }, {
+ keys: [
+ 'count', 'probability', 'category',
+ 'categorycount', 'colorcount', 'bandcolorcount'
+ ],
+ description: [
+ 'This value here applies when hovering over dimensions.',
+ 'Note tath `*categorycount`, *colorcount* and *bandcolorcount*',
+ 'are only available when `hoveron` contains the *color* flag'
+ ].join(' ')
+ }),
+
arrangement: {
valType: 'enumerated',
values: ['perpendicular', 'freeform', 'fixed'],
diff --git a/src/traces/parcats/defaults.js b/src/traces/parcats/defaults.js
index 3cdfb817680..7d22714cab6 100644
--- a/src/traces/parcats/defaults.js
+++ b/src/traces/parcats/defaults.js
@@ -18,8 +18,9 @@ var attributes = require('./attributes');
var mergeLength = require('../parcoords/merge_length');
function handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce) {
-
coerce('line.shape');
+ coerce('line.hovertemplate');
+
var lineColor = coerce('line.color', layout.colorway[0]);
if(hasColorscale(traceIn, 'line') && Lib.isArrayOrTypedArray(lineColor)) {
if(lineColor.length) {
@@ -96,6 +97,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
mergeLength(traceOut, dimensions, 'values', len);
coerce('hoveron');
+ coerce('hovertemplate');
coerce('arrangement');
coerce('bundlecolors');
coerce('sortpaths');
diff --git a/src/traces/parcats/parcats.js b/src/traces/parcats/parcats.js
index 36783ccf3a1..96734034ec5 100644
--- a/src/traces/parcats/parcats.js
+++ b/src/traces/parcats/parcats.js
@@ -413,6 +413,7 @@ function mouseoverPath(d) {
// Label
var gd = d.parcatsViewModel.graphDiv;
+ var trace = d.parcatsViewModel.trace;
var fullLayout = gd._fullLayout;
var rootBBox = fullLayout._paperdiv.node().getBoundingClientRect();
var graphDivBBox = d.parcatsViewModel.graphDiv.getBoundingClientRect();
@@ -438,19 +439,27 @@ function mouseoverPath(d) {
var textColor = tinycolor.mostReadable(d.model.color, ['black', 'white']);
+ var count = d.model.count;
+ var prob = count / d.parcatsViewModel.model.count;
+ var labels = {
+ countLabel: count,
+ probabilityLabel: prob.toFixed(3)
+ };
+
// Build hover text
var hovertextParts = [];
if(d.parcatsViewModel.hoverinfoItems.indexOf('count') !== -1) {
- hovertextParts.push(['Count:', d.model.count].join(' '));
+ hovertextParts.push(['Count:', labels.countLabel].join(' '));
}
if(d.parcatsViewModel.hoverinfoItems.indexOf('probability') !== -1) {
- hovertextParts.push(['P:', (d.model.count / d.parcatsViewModel.model.count).toFixed(3)].join(' '));
+ hovertextParts.push(['P:', labels.probabilityLabel].join(' '));
}
var hovertext = hovertextParts.join('
');
var mouseX = d3.mouse(gd)[0];
Fx.loneHover({
+ trace: trace,
x: hoverCenterX - rootBBox.left + graphDivBBox.left,
y: hoverCenterY - rootBBox.top + graphDivBBox.top,
text: hovertext,
@@ -459,7 +468,15 @@ function mouseoverPath(d) {
fontFamily: 'Monaco, "Courier New", monospace',
fontSize: 10,
fontColor: textColor,
- idealAlign: mouseX < hoverCenterX ? 'right' : 'left'
+ idealAlign: mouseX < hoverCenterX ? 'right' : 'left',
+ hovertemplate: (trace.line || {}).hovertemplate,
+ hovertemplateLabels: labels,
+ eventData: [{
+ data: trace._input,
+ fullData: trace,
+ count: count,
+ probability: prob
+ }]
}, {
container: fullLayout._hoverlayer.node(),
outerContainer: fullLayout._paper.node(),
@@ -715,6 +732,7 @@ function createHoverLabelForCategoryHovermode(rootBBox, bandElement) {
var catViewModel = rectSelection.datum();
var parcatsViewModel = catViewModel.parcatsViewModel;
var dimensionModel = parcatsViewModel.model.dimensions[catViewModel.model.dimensionInd];
+ var trace = parcatsViewModel.trace;
// Positions
var hoverCenterY = rectBoundingBox.top + rectBoundingBox.height / 2;
@@ -732,19 +750,27 @@ function createHoverLabelForCategoryHovermode(rootBBox, bandElement) {
hoverLabelIdealAlign = 'right';
}
+ var count = catViewModel.model.count;
+ var catLabel = catViewModel.model.categoryLabel;
+ var prob = count / catViewModel.parcatsViewModel.model.count;
+ var labels = {
+ countLabel: count,
+ categoryLabel: catLabel,
+ probabilityLabel: prob.toFixed(3)
+ };
+
// Hover label text
var hoverinfoParts = [];
if(catViewModel.parcatsViewModel.hoverinfoItems.indexOf('count') !== -1) {
- hoverinfoParts.push(['Count:', catViewModel.model.count].join(' '));
+ hoverinfoParts.push(['Count:', labels.countLabel].join(' '));
}
if(catViewModel.parcatsViewModel.hoverinfoItems.indexOf('probability') !== -1) {
- hoverinfoParts.push([
- 'P(' + catViewModel.model.categoryLabel + '):',
- (catViewModel.model.count / catViewModel.parcatsViewModel.model.count).toFixed(3)].join(' '));
+ hoverinfoParts.push(['P(' + labels.categoryLabel + '):', labels.probabilityLabel].join(' '));
}
var hovertext = hoverinfoParts.join('
');
return {
+ trace: trace,
x: hoverCenterX - rootBBox.left,
y: hoverCenterY - rootBBox.top,
text: hovertext,
@@ -753,7 +779,16 @@ function createHoverLabelForCategoryHovermode(rootBBox, bandElement) {
fontFamily: 'Monaco, "Courier New", monospace',
fontSize: 12,
fontColor: 'black',
- idealAlign: hoverLabelIdealAlign
+ idealAlign: hoverLabelIdealAlign,
+ hovertemplate: trace.hovertemplate,
+ hovertemplateLabels: labels,
+ eventData: [{
+ data: trace._input,
+ fullData: trace,
+ count: count,
+ category: catLabel,
+ probability: prob
+ }]
};
}
@@ -800,6 +835,7 @@ function createHoverLabelForColorHovermode(rootBBox, bandElement) {
var catViewModel = bandViewModel.categoryViewModel;
var parcatsViewModel = catViewModel.parcatsViewModel;
var dimensionModel = parcatsViewModel.model.dimensions[catViewModel.model.dimensionInd];
+ var trace = parcatsViewModel.trace;
// positions
var hoverCenterY = bandBoundingBox.y + bandBoundingBox.height / 2;
@@ -840,26 +876,25 @@ function createHoverLabelForColorHovermode(rootBBox, bandElement) {
}
});
+ var pColorAndCat = bandColorCount / totalCount;
+ var pCatGivenColor = bandColorCount / colorCount;
+ var pColorGivenCat = bandColorCount / catCount;
+
+ var labels = {
+ countLabel: totalCount,
+ categoryLabel: catLabel,
+ probabilityLabel: pColorAndCat.toFixed(3)
+ };
+
// Hover label text
var hoverinfoParts = [];
if(catViewModel.parcatsViewModel.hoverinfoItems.indexOf('count') !== -1) {
- hoverinfoParts.push(['Count:', bandColorCount].join(' '));
+ hoverinfoParts.push(['Count:', labels.countLabel].join(' '));
}
if(catViewModel.parcatsViewModel.hoverinfoItems.indexOf('probability') !== -1) {
- var pColorAndCatLable = 'P(color ∩ ' + catLabel + '): ';
- var pColorAndCatValue = (bandColorCount / totalCount).toFixed(3);
- var pColorAndCatRow = pColorAndCatLable + pColorAndCatValue;
- hoverinfoParts.push(pColorAndCatRow);
-
- var pCatGivenColorLabel = 'P(' + catLabel + ' | color): ';
- var pCatGivenColorValue = (bandColorCount / colorCount).toFixed(3);
- var pCatGivenColorRow = pCatGivenColorLabel + pCatGivenColorValue;
- hoverinfoParts.push(pCatGivenColorRow);
-
- var pColorGivenCatLabel = 'P(color | ' + catLabel + '): ';
- var pColorGivenCatValue = (bandColorCount / catCount).toFixed(3);
- var pColorGivenCatRow = pColorGivenCatLabel + pColorGivenCatValue;
- hoverinfoParts.push(pColorGivenCatRow);
+ hoverinfoParts.push('P(color ∩ ' + catLabel + '): ' + labels.probabilityLabel);
+ hoverinfoParts.push('P(' + catLabel + ' | color): ' + pCatGivenColor.toFixed(3));
+ hoverinfoParts.push('P(color | ' + catLabel + '): ' + pColorGivenCat.toFixed(3));
}
var hovertext = hoverinfoParts.join('
');
@@ -868,6 +903,7 @@ function createHoverLabelForColorHovermode(rootBBox, bandElement) {
var textColor = tinycolor.mostReadable(bandViewModel.color, ['black', 'white']);
return {
+ trace: trace,
x: hoverCenterX - rootBBox.left,
y: hoverCenterY - rootBBox.top,
// name: 'NAME',
@@ -877,7 +913,19 @@ function createHoverLabelForColorHovermode(rootBBox, bandElement) {
fontFamily: 'Monaco, "Courier New", monospace',
fontColor: textColor,
fontSize: 10,
- idealAlign: hoverLabelIdealAlign
+ idealAlign: hoverLabelIdealAlign,
+ hovertemplate: trace.hovertemplate,
+ hovertemplateLabels: labels,
+ eventData: [{
+ data: trace._input,
+ fullData: trace,
+ category: catLabel,
+ count: totalCount,
+ probability: pColorAndCat,
+ categorycount: catCount,
+ colorcount: colorCount,
+ bandcolorcount: bandColorCount
+ }]
};
}
@@ -1475,12 +1523,13 @@ function createParcatsViewModel(graphDiv, layout, wrappedParcatsModel) {
if(trace.hoverinfo === 'all') {
hoverinfoItems = ['count', 'probability'];
} else {
- hoverinfoItems = trace.hoverinfo.split('+');
+ hoverinfoItems = (trace.hoverinfo || '').split('+');
}
// Construct parcatsViewModel
// --------------------------
var parcatsViewModel = {
+ trace: trace,
key: trace.uid,
model: parcatsModel,
x: traceX,
diff --git a/src/traces/scatter3d/attributes.js b/src/traces/scatter3d/attributes.js
index 9c4e2910711..d02559bf30c 100644
--- a/src/traces/scatter3d/attributes.js
+++ b/src/traces/scatter3d/attributes.js
@@ -10,6 +10,7 @@
var scatterAttrs = require('../scatter/attributes');
var colorAttributes = require('../../components/colorscale/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var baseAttrs = require('../../plots/attributes');
var DASHES = require('../../constants/gl3d_dashes');
@@ -94,6 +95,7 @@ var attrs = module.exports = overrideAll({
'To be seen, trace `hoverinfo` must contain a *text* flag.'
].join(' ')
}),
+ hovertemplate: hovertemplateAttrs(),
mode: extendFlat({}, scatterAttrs.mode, // shouldn't this be on-par with 2D?
{dflt: 'lines+markers'}),
diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js
index acf3345ea82..0b20888395a 100644
--- a/src/traces/scatter3d/defaults.js
+++ b/src/traces/scatter3d/defaults.js
@@ -33,6 +33,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
coerce('text');
coerce('hovertext');
+ coerce('hovertemplate');
coerce('mode');
if(subTypes.hasLines(traceOut)) {
diff --git a/src/traces/scattercarpet/attributes.js b/src/traces/scattercarpet/attributes.js
index 20d61eaae3d..594b8407d05 100644
--- a/src/traces/scattercarpet/attributes.js
+++ b/src/traces/scattercarpet/attributes.js
@@ -10,6 +10,7 @@
var scatterAttrs = require('../scatter/attributes');
var plotAttrs = require('../../plots/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var colorAttributes = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
@@ -118,4 +119,5 @@ module.exports = {
flags: ['a', 'b', 'text', 'name']
}),
hoveron: scatterAttrs.hoveron,
+ hovertemplate: hovertemplateAttrs()
};
diff --git a/src/traces/scattercarpet/defaults.js b/src/traces/scattercarpet/defaults.js
index c330e773b96..61d28ba75b5 100644
--- a/src/traces/scattercarpet/defaults.js
+++ b/src/traces/scattercarpet/defaults.js
@@ -78,7 +78,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
if(traceOut.fill === 'tonext' || traceOut.fill === 'toself') {
dfltHoverOn.push('fills');
}
- coerce('hoveron', dfltHoverOn.join('+') || 'points');
+
+ var hoverOn = coerce('hoveron', dfltHoverOn.join('+') || 'points');
+ if(hoverOn !== 'fills') coerce('hovertemplate');
Lib.coerceSelectionMarkerOpacity(traceOut, coerce);
};
diff --git a/src/traces/scattercarpet/event_data.js b/src/traces/scattercarpet/event_data.js
index a1ab85a1b6e..7cb05de5ded 100644
--- a/src/traces/scattercarpet/event_data.js
+++ b/src/traces/scattercarpet/event_data.js
@@ -13,6 +13,7 @@ module.exports = function eventData(out, pt, trace, cd, pointNumber) {
out.a = cdi.a;
out.b = cdi.b;
+ out.y = cdi.y;
return out;
};
diff --git a/src/traces/scattercarpet/hover.js b/src/traces/scattercarpet/hover.js
index f8b47447a39..16882588ca8 100644
--- a/src/traces/scattercarpet/hover.js
+++ b/src/traces/scattercarpet/hover.js
@@ -48,8 +48,15 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
var trace = newPointData.trace;
var carpet = trace._carpet;
- var hoverinfo = cdi.hi || trace.hoverinfo;
- var parts = hoverinfo.split('+');
+
+ var ij = carpet.ab2ij([cdi.a, cdi.b]);
+ var i0 = Math.floor(ij[0]);
+ var ti = ij[0] - i0;
+ var j0 = Math.floor(ij[1]);
+ var tj = ij[1] - j0;
+ var xy = carpet.evalxy([], i0, j0, ti, tj);
+ newPointData.yLabel = xy[1].toFixed(3);
+
var text = [];
function textPart(ax, val) {
@@ -64,21 +71,19 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
text.push(prefix + ': ' + val.toFixed(3) + ax.labelsuffix);
}
- if(parts.indexOf('all') !== -1) parts = ['a', 'b'];
- if(parts.indexOf('a') !== -1) textPart(carpet.aaxis, cdi.a);
- if(parts.indexOf('b') !== -1) textPart(carpet.baxis, cdi.b);
- var ij = carpet.ab2ij([cdi.a, cdi.b]);
- var i0 = Math.floor(ij[0]);
- var ti = ij[0] - i0;
+ if(!trace.hovertemplate) {
+ var hoverinfo = cdi.hi || trace.hoverinfo;
+ var parts = hoverinfo.split('+');
- var j0 = Math.floor(ij[1]);
- var tj = ij[1] - j0;
+ if(parts.indexOf('all') !== -1) parts = ['a', 'b'];
+ if(parts.indexOf('a') !== -1) textPart(carpet.aaxis, cdi.a);
+ if(parts.indexOf('b') !== -1) textPart(carpet.baxis, cdi.b);
- var xy = carpet.evalxy([], i0, j0, ti, tj);
- text.push('y: ' + xy[1].toFixed(3));
+ text.push('y: ' + newPointData.yLabel);
- newPointData.extraText = text.join('
');
+ newPointData.extraText = text.join('
');
+ }
return scatterPointData;
};
diff --git a/src/traces/splom/attributes.js b/src/traces/splom/attributes.js
index 38ca5188545..79dff01442a 100644
--- a/src/traces/splom/attributes.js
+++ b/src/traces/splom/attributes.js
@@ -10,6 +10,7 @@
var scatterAttrs = require('../scatter/attributes');
var colorAttrs = require('../../components/colorscale/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var scatterGlAttrs = require('../scattergl/attributes');
var cartesianIdRegex = require('../../plots/cartesian/constants').idRegex;
var templatedArray = require('../../plot_api/plot_template').templatedArray;
@@ -123,6 +124,7 @@ module.exports = {
'this trace\'s (x,y) coordinates.'
].join(' ')
}),
+ hovertemplate: hovertemplateAttrs(),
marker: markerAttrs,
diff --git a/src/traces/splom/defaults.js b/src/traces/splom/defaults.js
index d363d299964..1a268cef365 100644
--- a/src/traces/splom/defaults.js
+++ b/src/traces/splom/defaults.js
@@ -39,6 +39,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
}
coerce('text');
+ coerce('hovertemplate');
handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce);
diff --git a/src/traces/streamtube/attributes.js b/src/traces/streamtube/attributes.js
index e7ee464ff2c..4dfbd303912 100644
--- a/src/traces/streamtube/attributes.js
+++ b/src/traces/streamtube/attributes.js
@@ -10,6 +10,7 @@
var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var mesh3dAttrs = require('../mesh3d/attributes');
var baseAttrs = require('../../plots/attributes');
@@ -130,7 +131,14 @@ var attrs = {
'this text element will be seen in all hover labels.',
'Note that streamtube traces do not support array `text` values.'
].join(' ')
- }
+ },
+ hovertemplate: hovertemplateAttrs({editType: 'calc'}, {
+ keys: [
+ 'tubex', 'tubey', 'tubez',
+ 'tubeu', 'tubev', 'tubew',
+ 'norm', 'divergence'
+ ]
+ })
};
extendFlat(attrs, colorscaleAttrs('', {
diff --git a/src/traces/streamtube/defaults.js b/src/traces/streamtube/defaults.js
index ffc6cd2d04c..e8bae6160d5 100644
--- a/src/traces/streamtube/defaults.js
+++ b/src/traces/streamtube/defaults.js
@@ -53,6 +53,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'c'});
coerce('text');
+ coerce('hovertemplate');
// disable 1D transforms (for now)
// x/y/z and u/v/w have matching lengths,
diff --git a/src/traces/surface/attributes.js b/src/traces/surface/attributes.js
index b12b1555333..fd19ffd3295 100644
--- a/src/traces/surface/attributes.js
+++ b/src/traces/surface/attributes.js
@@ -11,6 +11,7 @@
var Color = require('../../components/color');
var colorscaleAttrs = require('../../components/colorscale/attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
+var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var baseAttrs = require('../../plots/attributes');
var extendFlat = require('../../lib/extend').extendFlat;
@@ -123,6 +124,7 @@ var attrs = module.exports = overrideAll(extendFlat({
'these elements will be seen in the hover labels.'
].join(' ')
},
+ hovertemplate: hovertemplateAttrs(),
surfacecolor: {
valType: 'data_array',
diff --git a/src/traces/surface/defaults.js b/src/traces/surface/defaults.js
index 54c0ffc5033..3ca1cf856db 100644
--- a/src/traces/surface/defaults.js
+++ b/src/traces/surface/defaults.js
@@ -42,6 +42,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout);
coerce('text');
+ coerce('hovertemplate');
// Coerce remaining properties
[
diff --git a/test/jasmine/tests/carpet_test.js b/test/jasmine/tests/carpet_test.js
index bc8954643a6..ffc385f940c 100644
--- a/test/jasmine/tests/carpet_test.js
+++ b/test/jasmine/tests/carpet_test.js
@@ -678,6 +678,28 @@ describe('scattercarpet hover labels', function() {
)
.then(done);
});
+
+ it('should generate hover label with *hovertemplate*', function(done) {
+ var fig = Lib.extendDeep({}, require('@mocks/scattercarpet.json'));
+ fig.data[5].hovertemplate = 'f(%{a}, %{b}) = %{y}scattercarpet #%{curveNumber}';
+
+ run(
+ [200, 200], fig,
+ [['f(0.2, 3.5) = 2.900'], 'scattercarpet #5']
+ )
+ .then(done);
+ });
+
+ it('should generate hover label with arrayOk *hovertemplate*', function(done) {
+ var fig = Lib.extendDeep({}, require('@mocks/scattercarpet.json'));
+ fig.data[5].hovertemplate = ['', '', '', 'f(%{a}, %{b}) = %{y:.1f}pt #%{pointNumber}'];
+
+ run(
+ [200, 200], fig,
+ [['f(0.2, 3.5) = 3.0'], 'pt #3']
+ )
+ .then(done);
+ });
});
describe('contourcarpet plotting & editing', function() {
diff --git a/test/jasmine/tests/cone_test.js b/test/jasmine/tests/cone_test.js
index 333e26c2b3d..f43b106666b 100644
--- a/test/jasmine/tests/cone_test.js
+++ b/test/jasmine/tests/cone_test.js
@@ -297,6 +297,16 @@ describe('Test cone interactions', function() {
'norm: 3.00'
].join('\n')
});
+
+ return Plotly.restyle(gd, 'hovertemplate', 'NORM : %{norm}
at %{x},%{y},%{z}LOOKOUT');
+ })
+ .then(delay(20))
+ .then(_hover)
+ .then(function() {
+ assertHoverLabelContent({
+ name: 'LOOKOUT',
+ nums: 'NORM : 3.00\nat 2,2,2'
+ });
})
.catch(failTest)
.then(done);
diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js
index 382616c048f..91c881f5faf 100644
--- a/test/jasmine/tests/gl3d_plot_interact_test.js
+++ b/test/jasmine/tests/gl3d_plot_interact_test.js
@@ -242,6 +242,11 @@ describe('Test gl3d plots', function() {
})
.then(function() {
assertHoverText(null, null, '100k');
+
+ return Plotly.restyle(gd, 'hovertemplate', 'THIS Y -- %{y}');
+ })
+ .then(function() {
+ assertHoverText(null, null, null, 'THIS Y -- c');
})
.catch(failTest)
.then(done);
@@ -339,6 +344,11 @@ describe('Test gl3d plots', function() {
})
.then(function() {
assertHoverText(null, null, null, 'yo!');
+
+ return Plotly.restyle(gd, 'hovertemplate', '!!! %{z} !!!');
+ })
+ .then(function() {
+ assertHoverText(null, null, null, '!!! 43 !!!');
})
.then(done);
});
@@ -418,6 +428,12 @@ describe('Test gl3d plots', function() {
.then(function() {
assertHoverText(null, null, null, 'yo!');
})
+ .then(function() {
+ return Plotly.restyle(gd, 'hovertemplate', '%{x}-%{y}-%{z}');
+ })
+ .then(function() {
+ assertHoverText(null, null, null, '3-4-5');
+ })
.catch(failTest)
.then(done);
});
diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js
index 5d025b8c1d8..93d6feb0c58 100644
--- a/test/jasmine/tests/hover_label_test.js
+++ b/test/jasmine/tests/hover_label_test.js
@@ -766,6 +766,22 @@ describe('hover info', function() {
name: 'one'
});
})
+ .then(function() {
+ return Plotly.restyle(gd, 'hovertemplate', '(%{x},%{y}) -- %{z}trace %{data.name}');
+ })
+ .then(function() {
+ _hover(gd, 250, 50);
+ assertHoverLabelContent({
+ nums: '(1,3) -- 2',
+ name: 'trace two'
+ });
+
+ _hover(gd, 250, 300);
+ assertHoverLabelContent({
+ nums: '(1,1) -- 5.56',
+ name: 'trace one'
+ });
+ })
.catch(failTest)
.then(done);
});
@@ -874,6 +890,22 @@ describe('hover info', function() {
name: 'one'
});
})
+ .then(function() {
+ return Plotly.restyle(gd, 'hovertemplate', 'f(%{x:.3f},%{y:.3f})=%{z}');
+ })
+ .then(function() {
+ _hover(gd, 250, 100);
+ assertHoverLabelContent({
+ nums: 'f(1.000,3.000)=2',
+ name: 'two'
+ });
+
+ _hover(gd, 250, 300);
+ assertHoverLabelContent({
+ nums: 'f(1.000,1.000)=5.56',
+ name: 'one'
+ });
+ })
.catch(failTest)
.then(done);
});
@@ -912,6 +944,22 @@ describe('hover info', function() {
name: 'one'
});
})
+ .then(function() {
+ return Plotly.restyle(gd, 'hovertemplate', 'f(%{x:.1f}, %{y:.1f})=%{z}');
+ })
+ .then(function() {
+ _hover(gd, 250, 50);
+ assertHoverLabelContent({
+ nums: 'f(1.0, 3.0)=2',
+ name: ''
+ });
+
+ _hover(gd, 250, 270);
+ assertHoverLabelContent({
+ nums: 'f(1.0, 1.0)=5.56',
+ name: ''
+ });
+ })
.catch(failTest)
.then(done);
});
@@ -2182,6 +2230,17 @@ describe('Hover on multicategory axes', function() {
});
expect(eventData.x).toEqual(['2017', 'q3']);
})
+ .then(function() {
+ return Plotly.restyle(gd, 'hovertemplate', '%{z} @ %{x} | %{y}');
+ })
+ .then(function() { _hover(200, 200); })
+ .then(function() {
+ assertHoverLabelContent({
+ nums: '2.303 @ 2017 - q3 | Group 3 - A',
+ name: 'w/ 2d z'
+ });
+ expect(eventData.x).toEqual(['2017', 'q3']);
+ })
.catch(failTest)
.then(done);
});
diff --git a/test/jasmine/tests/isosurface_test.js b/test/jasmine/tests/isosurface_test.js
index b745fbbba49..3b821063aa8 100644
--- a/test/jasmine/tests/isosurface_test.js
+++ b/test/jasmine/tests/isosurface_test.js
@@ -398,6 +398,17 @@ describe('Test isosurface', function() {
].join('\n')
});
})
+ .then(function() {
+ return Plotly.restyle(gd, 'hovertemplate', '%{value}
(%{x},%{y},%{z})!!');
+ })
+ .then(delay(20))
+ .then(_hover4)
+ .then(function() {
+ assertHoverLabelContent({
+ nums: '−1.3\n(0.4,100μ,−4)',
+ name: '!!'
+ });
+ })
.catch(failTest)
.then(done);
});
diff --git a/test/jasmine/tests/parcats_test.js b/test/jasmine/tests/parcats_test.js
index 674555bccb7..8570f3ed7e0 100644
--- a/test/jasmine/tests/parcats_test.js
+++ b/test/jasmine/tests/parcats_test.js
@@ -1,5 +1,6 @@
var Plotly = require('@lib/index');
var Lib = require('@src/lib');
+
var d3 = require('d3');
var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
@@ -8,6 +9,9 @@ var mouseEvent = require('../assets/mouse_event');
var click = require('../assets/click');
var delay = require('../assets/delay');
+var customAssertions = require('../assets/custom_assertions');
+var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
+
var CALLBACK_DELAY = 500;
// Testing constants
@@ -1726,3 +1730,71 @@ describe('Hover events with hoveron color', function() {
.then(done);
});
});
+
+describe('Parcats hover:', function() {
+ var gd;
+
+ afterEach(destroyGraphDiv);
+
+ function run(s, done) {
+ gd = createGraphDiv();
+
+ var fig = Lib.extendDeep({},
+ s.mock || require('@mocks/parcats_basic.json')
+ );
+ if(s.patch) fig = s.patch(fig);
+
+ return Plotly.plot(gd, fig).then(function() {
+ mouseEvent('mousemove', s.pos[0], s.pos[1]);
+ mouseEvent('mouseover', s.pos[0], s.pos[1]);
+
+ setTimeout(function() {
+ assertHoverLabelContent(s);
+ done();
+ }, CALLBACK_DELAY);
+
+ })
+ .catch(failTest);
+ }
+
+ var dimPos = [320, 310];
+ var linePos = [272, 415];
+
+ var specs = [{
+ desc: 'basic - on dimension',
+ pos: dimPos,
+ nums: 'Count: 9',
+ name: ''
+ }, {
+ desc: 'basic - on line',
+ pos: linePos,
+ nums: 'Count: 1',
+ name: ''
+ }, {
+ desc: 'with hovetemplate - on dimension',
+ pos: dimPos,
+ patch: function(fig) {
+ fig.data[0].hovertemplate = 'probz=%{probability:.1f}LOOK';
+ return fig;
+ },
+ nums: 'probz=1.0',
+ name: 'LOOK'
+ }, {
+ desc: 'with hovertemplate - on line',
+ pos: linePos,
+ patch: function(fig) {
+ fig.data[0].line = {
+ hovertemplate: 'P=%{probability}with count=%{count}'
+ };
+ return fig;
+ },
+ nums: 'P=0.111',
+ name: 'with count=1'
+ }];
+
+ specs.forEach(function(s) {
+ it('should generate correct hover labels ' + s.desc, function(done) {
+ run(s, done);
+ });
+ });
+});
diff --git a/test/jasmine/tests/splom_test.js b/test/jasmine/tests/splom_test.js
index 3e09444efd9..815671aa976 100644
--- a/test/jasmine/tests/splom_test.js
+++ b/test/jasmine/tests/splom_test.js
@@ -1360,6 +1360,18 @@ describe('Test splom hover:', function() {
nums: 'Apr 2003',
axis: 'Jan 2000',
evtPts: [{x: '2000-01-01', y: '2003-04-21', pointNumber: 0}]
+ }, {
+ desc: 'with a hovertemplate',
+ patch: function(fig) {
+ fig.data.forEach(function(t) {
+ t.hovertemplate = '%{x}|%{y}pt %{pointNumber}';
+ });
+ fig.layout.hovermode = 'closest';
+ return fig;
+ },
+ nums: '2.6|7.7',
+ name: 'pt 18',
+ evtPts: [{x: 2.6, y: 7.7, pointNumber: 18, curveNumber: 2}]
}];
specs.forEach(function(s) {
diff --git a/test/jasmine/tests/streamtube_test.js b/test/jasmine/tests/streamtube_test.js
index 647fb49f5a2..1316e90c92e 100644
--- a/test/jasmine/tests/streamtube_test.js
+++ b/test/jasmine/tests/streamtube_test.js
@@ -395,6 +395,14 @@ describe('@noCI Test streamtube hover', function() {
].join('\n'),
name: 'TUBE!'
});
+
+ return Plotly.restyle(gd, 'hovertemplate', '∇·F = %{divergence:.3f}TUBE');
+ })
+ .then(function() {
+ assertHoverLabelContent({
+ nums: '∇·F = 0.465',
+ name: 'TUBE'
+ });
})
.catch(failTest)
.then(done);