diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index cc366193eb3..4888f353cf2 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1073,7 +1073,11 @@ function cleanPoint(d, hovermode) { d.yLabel = ('yLabel' in d) ? d.yLabel : Axes.hoverLabelText(d.ya, d.yLabelVal); d.yVal = d.ya.c2d(d.yLabelVal); } - if(d.zLabelVal !== undefined) d.zLabel = String(d.zLabelVal); + + // Traces like heatmaps generate the zLabel in their hoverPoints function + if(d.zLabelVal !== undefined && d.zLabel === undefined) { + d.zLabel = String(d.zLabelVal); + } // for box means and error bars, add the range to the label if(!isNaN(d.xerr) && !(d.xa.type === 'log' && d.xerr <= 0)) { diff --git a/src/traces/contour/attributes.js b/src/traces/contour/attributes.js index 5414afe9f48..8db880b2317 100644 --- a/src/traces/contour/attributes.js +++ b/src/traces/contour/attributes.js @@ -30,6 +30,7 @@ module.exports = extendFlat({ transpose: heatmapAttrs.transpose, xtype: heatmapAttrs.xtype, ytype: heatmapAttrs.ytype, + zhoverformat: heatmapAttrs.zhoverformat, connectgaps: heatmapAttrs.connectgaps, diff --git a/src/traces/contour/defaults.js b/src/traces/contour/defaults.js index a616f818045..1e30d6b17c5 100644 --- a/src/traces/contour/defaults.js +++ b/src/traces/contour/defaults.js @@ -34,4 +34,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout handleContoursDefaults(traceIn, traceOut, coerce); handleStyleDefaults(traceIn, traceOut, coerce, layout); + + coerce('zhoverformat'); + // Needed for formatting of hoverlabel if format is not explicitly specified + traceOut._separators = layout.separators; }; diff --git a/src/traces/heatmap/attributes.js b/src/traces/heatmap/attributes.js index 372197a5f7b..e967f17860a 100644 --- a/src/traces/heatmap/attributes.js +++ b/src/traces/heatmap/attributes.js @@ -100,6 +100,17 @@ module.exports = extendFlat({}, { editType: 'plot', description: 'Sets the vertical gap (in pixels) between bricks.' }, + zhoverformat: { + valType: 'string', + dflt: '', + role: 'style', + editType: 'none', + description: [ + 'Sets the hover text formatting rule using d3 formatting mini-languages', + 'which are very similar to those in Python. See:', + 'https://github.com/d3/d3-format/blob/master/README.md#locale_format' + ].join(' ') + }, }, colorscaleAttrs, { autocolorscale: extendFlat({}, colorscaleAttrs.autocolorscale, {dflt: false}) }, diff --git a/src/traces/heatmap/defaults.js b/src/traces/heatmap/defaults.js index 3dbbaa0f380..b80c76d6e2b 100644 --- a/src/traces/heatmap/defaults.js +++ b/src/traces/heatmap/defaults.js @@ -40,4 +40,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('connectgaps', hasColumns(traceOut) && (traceOut.zsmooth !== false)); colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}); + + coerce('zhoverformat'); + traceOut._separators = layout.separators; // Needed for formatting of hoverlabel if format is not explicitly specified }; diff --git a/src/traces/heatmap/hover.js b/src/traces/heatmap/hover.js index e3a870f3cb7..310f92c0b84 100644 --- a/src/traces/heatmap/hover.js +++ b/src/traces/heatmap/hover.js @@ -11,6 +11,7 @@ var Fx = require('../../components/fx'); var Lib = require('../../lib'); +var Axes = require('../../plots/cartesian/axes'); var MAXDIST = Fx.constants.MAXDIST; @@ -26,6 +27,9 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, contour) y = cd0.y, z = cd0.z, zmask = cd0.zmask, + range = [trace.zmin, trace.zmax], + zhoverformat = trace.zhoverformat, + _separators = trace._separators, x2 = x, y2 = y, xl, @@ -99,6 +103,17 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, contour) text = cd0.text[ny][nx]; } + var zLabel; + // dummy axis for formatting the z value + var dummyAx = { + type: 'linear', + range: range, + hoverformat: zhoverformat, + _separators: _separators + }; + var zLabelObj = Axes.tickText(dummyAx, zVal, 'hover'); + zLabel = zLabelObj.text; + return [Lib.extendFlat(pointData, { index: [ny, nx], // never let a 2D override 1D type as closest point @@ -110,6 +125,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, contour) xLabelVal: xl, yLabelVal: yl, zLabelVal: zVal, + zLabel: zLabel, text: text })]; }; diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index b6be796bdbe..b8fa0ad2693 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -499,6 +499,43 @@ describe('hover info', function() { .catch(fail) .then(done); }); + + it('should display correct label content with specified format', function(done) { + var gd = createGraphDiv(); + + Plotly.plot(gd, [{ + type: 'heatmap', + y: [0, 1], + z: [[1.11111, 2.2222, 3.33333], [4.44444, 5.55555, 6.66666]], + name: 'one', + zhoverformat: '.2f' + }, { + type: 'heatmap', + y: [2, 3], + z: [[1, 2, 3], [2, 2, 1]], + name: 'two' + }], { + width: 500, + height: 400, + margin: {l: 0, t: 0, r: 0, b: 0} + }) + .then(function() { + _hover(gd, 250, 100); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2', + name: 'two' + }); + }) + .then(function() { + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: 'x: 1\ny: 1\nz: 5.56', + name: 'one' + }); + }) + .catch(fail) + .then(done); + }); }); describe('hoverformat', function() { diff --git a/test/jasmine/tests/select_test.js b/test/jasmine/tests/select_test.js index 9915cb9583f..72f29593e3f 100644 --- a/test/jasmine/tests/select_test.js +++ b/test/jasmine/tests/select_test.js @@ -717,10 +717,11 @@ describe('Test select box and lasso per trace:', function() { addInvisible(fig, false); // add a trace with no locations which will then make trace invisible, lacking DOM elements - fig.data.push(Lib.extendDeep({}, fig.data[0])); - fig.data[1].text = []; - fig.data[1].locations = []; - fig.data[1].z = []; + var emptyChoroplethTrace = Lib.extendDeep({}, fig.data[0]); + emptyChoroplethTrace.text = []; + emptyChoroplethTrace.locations = []; + emptyChoroplethTrace.z = []; + fig.data.push(emptyChoroplethTrace); Plotly.plot(gd, fig) .then(function() {