diff --git a/src/lib/index.js b/src/lib/index.js
index 7a650690a02..ed572a0cdcc 100644
--- a/src/lib/index.js
+++ b/src/lib/index.js
@@ -1127,3 +1127,34 @@ lib.pseudoRandom = function() {
if(Math.abs(randSeed - lastVal) < 429496729) return lib.pseudoRandom();
return randSeed / 4294967296;
};
+
+
+/** Fill hover 'pointData' container with 'correct' hover text value
+ *
+ * - If trace hoverinfo contains a 'text' flag and hovertext is not set,
+ * the text elements will be seen in the hover labels.
+ *
+ * - If trace hoverinfo contains a 'text' flag and hovertext is set,
+ * hovertext takes precedence over text
+ * i.e. the hoverinfo elements will be seen in the hover labels
+ *
+ * @param {object} calcPt
+ * @param {object} trace
+ * @param {object || array} contOut (mutated here)
+ */
+lib.fillText = function(calcPt, trace, contOut) {
+ var fill = Array.isArray(contOut) ?
+ function(v) { contOut.push(v); } :
+ function(v) { contOut.text = v; };
+
+ var htx = lib.extractOption(calcPt, trace, 'htx', 'hovertext');
+ if(lib.isValidTextValue(htx)) return fill(htx);
+
+ var tx = lib.extractOption(calcPt, trace, 'tx', 'text');
+ if(lib.isValidTextValue(tx)) return fill(tx);
+};
+
+// accept all truthy values and 0 (which gets cast to '0' in the hover labels)
+lib.isValidTextValue = function(v) {
+ return v || v === 0;
+};
diff --git a/src/plots/gl3d/camera.js b/src/plots/gl3d/camera.js
deleted file mode 100644
index 13fdcaccb33..00000000000
--- a/src/plots/gl3d/camera.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
-* Copyright 2012-2019, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
diff --git a/src/traces/bar/hover.js b/src/traces/bar/hover.js
index c6dbce18c78..68d5fb818d7 100644
--- a/src/traces/bar/hover.js
+++ b/src/traces/bar/hover.js
@@ -12,7 +12,8 @@
var Fx = require('../../components/fx');
var Registry = require('../../registry');
var Color = require('../../components/color');
-var fillHoverText = require('../scatter/fill_hover_text');
+
+var fillText = require('../../lib').fillText;
function hoverPoints(pointData, xval, yval, hovermode) {
var barPointData = hoverOnBars(pointData, xval, yval, hovermode);
@@ -155,7 +156,7 @@ function hoverOnBars(pointData, xval, yval, hovermode) {
// in case of bars shifted within groups
pointData[posLetter + 'Spike'] = pa.c2p(di.p, true);
- fillHoverText(di, trace, pointData);
+ fillText(di, trace, pointData);
pointData.hovertemplate = trace.hovertemplate;
return pointData;
diff --git a/src/traces/barpolar/hover.js b/src/traces/barpolar/hover.js
index 2719f16725e..369c29ffa0c 100644
--- a/src/traces/barpolar/hover.js
+++ b/src/traces/barpolar/hover.js
@@ -11,7 +11,7 @@
var Fx = require('../../components/fx');
var Lib = require('../../lib');
var getTraceColor = require('../bar/hover').getTraceColor;
-var fillHoverText = require('../scatter/fill_hover_text');
+var fillText = Lib.fillText;
var makeHoverPointText = require('../scatterpolar/hover').makeHoverPointText;
var isPtInsidePolygon = require('../../plots/polar/helpers').isPtInsidePolygon;
@@ -59,7 +59,7 @@ module.exports = function hoverPoints(pointData, xval, yval) {
pointData.y0 = pointData.y1 = cdi.ct[1];
var _cdi = Lib.extendFlat({}, cdi, {r: cdi.s, theta: cdi.p});
- fillHoverText(cdi, trace, pointData);
+ fillText(cdi, trace, pointData);
makeHoverPointText(_cdi, trace, subplot, pointData);
pointData.hovertemplate = trace.hovertemplate;
pointData.color = getTraceColor(trace, cdi);
diff --git a/src/traces/box/hover.js b/src/traces/box/hover.js
index f5dd7f3e722..0d47ca02855 100644
--- a/src/traces/box/hover.js
+++ b/src/traces/box/hover.js
@@ -12,7 +12,7 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var Fx = require('../../components/fx');
var Color = require('../../components/color');
-var fillHoverText = require('../scatter/fill_hover_text');
+var fillText = Lib.fillText;
function hoverPoints(pointData, xval, yval, hovermode) {
var cd = pointData.cd;
@@ -270,7 +270,7 @@ function hoverOnPoints(pointData, xval, yval) {
var pLetter = pa._id.charAt(0);
closePtData[pLetter + 'Spike'] = pa.c2p(di.pos, true);
- fillHoverText(pt, trace, closePtData);
+ fillText(pt, trace, closePtData);
return closePtData;
}
diff --git a/src/traces/choropleth/hover.js b/src/traces/choropleth/hover.js
index 70211333f51..1071adbcecb 100644
--- a/src/traces/choropleth/hover.js
+++ b/src/traces/choropleth/hover.js
@@ -11,7 +11,7 @@
var Axes = require('../../plots/cartesian/axes');
var attributes = require('./attributes');
-var fillHoverText = require('../scatter/fill_hover_text');
+var fillText = require('../../lib').fillText;
module.exports = function hoverPoints(pointData, xval, yval) {
var cd = pointData.cd;
@@ -86,7 +86,7 @@ function makeHoverInfo(pointData, trace, pt, axis) {
if(hasZ) text.push(formatter(pt.z));
if(hasText) {
- fillHoverText(pt, trace, text);
+ fillText(pt, trace, text);
}
pointData.extraText = text.join('
');
diff --git a/src/traces/ohlc/hover.js b/src/traces/ohlc/hover.js
index 58e67165e6a..8243cacc6a8 100644
--- a/src/traces/ohlc/hover.js
+++ b/src/traces/ohlc/hover.js
@@ -12,7 +12,7 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var Fx = require('../../components/fx');
var Color = require('../../components/color');
-var fillHoverText = require('../scatter/fill_hover_text');
+var fillText = require('../../lib').fillText;
var DIRSYMBOL = {
increasing: '▲',
@@ -184,7 +184,7 @@ function hoverOnPoints(pointData, xval, yval, hovermode) {
getLabelLine('low'),
getLabelLine('close') + ' ' + DIRSYMBOL[dir]
] : [];
- if(hasText) fillHoverText(di, trace, textParts);
+ if(hasText) fillText(di, trace, textParts);
// don't make .yLabelVal or .text, since we're managing hoverinfo
// put it all in .extraText
diff --git a/src/traces/pie/calc.js b/src/traces/pie/calc.js
index f918f51ba30..03d7fa81ae3 100644
--- a/src/traces/pie/calc.js
+++ b/src/traces/pie/calc.js
@@ -14,6 +14,7 @@ var tinycolor = require('tinycolor2');
var Color = require('../../components/color');
var helpers = require('./helpers');
+var isValidTextValue = require('../../lib').isValidTextValue;
var pieExtendedColorWays = {};
@@ -99,8 +100,8 @@ function calc(gd, trace) {
pt = cd[i];
thisText = hasLabel ? [pt.label] : [];
if(hasText) {
- var texti = helpers.getFirstFilled(trace.text, pt.pts);
- if(texti) thisText.push(texti);
+ var tx = helpers.getFirstFilled(trace.text, pt.pts);
+ if(isValidTextValue(tx)) thisText.push(tx);
}
if(hasValue) thisText.push(helpers.formatPieValue(pt.v, separators));
if(hasPercent) thisText.push(helpers.formatPiePercent(pt.v / vTotal, separators));
diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js
index d940a5ecfb6..71167677eee 100644
--- a/src/traces/pie/plot.js
+++ b/src/traces/pie/plot.js
@@ -352,8 +352,8 @@ function attachFxHandlers(sliceTop, gd, cd) {
if(hoverinfo && hoverinfo.indexOf('label') !== -1) thisText.push(pt.label);
pt.text = helpers.castOption(trace2.hovertext || trace2.text, pt.pts);
if(hoverinfo && hoverinfo.indexOf('text') !== -1) {
- var texti = pt.text;
- if(texti) thisText.push(texti);
+ var tx = pt.text;
+ if(Lib.isValidTextValue(tx)) thisText.push(tx);
}
pt.value = pt.v;
pt.valueLabel = helpers.formatPieValue(pt.v, separators);
diff --git a/src/traces/scatter/fill_hover_text.js b/src/traces/scatter/fill_hover_text.js
deleted file mode 100644
index ca925844e8e..00000000000
--- a/src/traces/scatter/fill_hover_text.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
-* Copyright 2012-2019, Plotly, Inc.
-* All rights reserved.
-*
-* This source code is licensed under the MIT license found in the
-* LICENSE file in the root directory of this source tree.
-*/
-
-'use strict';
-
-var Lib = require('../../lib');
-
-/** Fill hover 'pointData' container with 'correct' hover text value
- *
- * - If trace hoverinfo contains a 'text' flag and hovertext is not set,
- * the text elements will be seen in the hover labels.
- *
- * - If trace hoverinfo contains a 'text' flag and hovertext is set,
- * hovertext takes precedence over text
- * i.e. the hoverinfo elements will be seen in the hover labels
- *
- * @param {object} calcPt
- * @param {object} trace
- * @param {object || array} contOut (mutated here)
- */
-module.exports = function fillHoverText(calcPt, trace, contOut) {
- var fill = Array.isArray(contOut) ?
- function(v) { contOut.push(v); } :
- function(v) { contOut.text = v; };
-
- var htx = Lib.extractOption(calcPt, trace, 'htx', 'hovertext');
- if(isValid(htx)) return fill(htx);
-
- var tx = Lib.extractOption(calcPt, trace, 'tx', 'text');
- if(isValid(tx)) return fill(tx);
-};
-
-// accept all truthy values and 0 (which gets cast to '0' in the hover labels)
-function isValid(v) {
- return v || v === 0;
-}
diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js
index f75f5308552..a56193059d6 100644
--- a/src/traces/scatter/hover.js
+++ b/src/traces/scatter/hover.js
@@ -13,7 +13,7 @@ var Fx = require('../../components/fx');
var Registry = require('../../registry');
var getTraceColor = require('./get_trace_color');
var Color = require('../../components/color');
-var fillHoverText = require('./fill_hover_text');
+var fillText = Lib.fillText;
module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
var cd = pointData.cd;
@@ -96,7 +96,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
hovertemplate: trace.hovertemplate
});
- fillHoverText(di, trace, pointData);
+ fillText(di, trace, pointData);
Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, pointData);
return [pointData];
diff --git a/src/traces/scattercarpet/hover.js b/src/traces/scattercarpet/hover.js
index 3f71bd0fbaf..74de3b8c581 100644
--- a/src/traces/scattercarpet/hover.js
+++ b/src/traces/scattercarpet/hover.js
@@ -9,7 +9,7 @@
'use strict';
var scatterHover = require('../scatter/hover');
-var fillHoverText = require('../scatter/fill_hover_text');
+var fillText = require('../../lib').fillText;
module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
var scatterPointData = scatterHover(pointData, xval, yval, hovermode);
@@ -84,7 +84,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) {
text.push('y: ' + newPointData.yLabel);
if(parts.indexOf('text') !== -1) {
- fillHoverText(cdi, trace, text);
+ fillText(cdi, trace, text);
}
newPointData.extraText = text.join('
');
diff --git a/src/traces/scattergeo/hover.js b/src/traces/scattergeo/hover.js
index 5aa9ed50c4b..37569731fe1 100644
--- a/src/traces/scattergeo/hover.js
+++ b/src/traces/scattergeo/hover.js
@@ -14,7 +14,7 @@ var Axes = require('../../plots/cartesian/axes');
var BADNUM = require('../../constants/numerical').BADNUM;
var getTraceColor = require('../scatter/get_trace_color');
-var fillHoverText = require('../scatter/fill_hover_text');
+var fillText = require('../../lib').fillText;
var attributes = require('./attributes');
module.exports = function hoverPoints(pointData, xval, yval) {
@@ -103,7 +103,7 @@ function getExtraText(trace, pt, axis, labels) {
}
if(hasText) {
- fillHoverText(pt, trace, text);
+ fillText(pt, trace, text);
}
return text.join('
');
diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js
index c6ea11ee6d4..3a655fdb22f 100644
--- a/src/traces/scattergl/index.js
+++ b/src/traces/scattergl/index.js
@@ -29,7 +29,7 @@ var setFirstScatter = scatterCalc.setFirstScatter;
var calcColorscale = require('../scatter/colorscale_calc');
var linkTraces = require('../scatter/link_traces');
var getTraceColor = require('../scatter/get_trace_color');
-var fillHoverText = require('../scatter/fill_hover_text');
+var fillText = Lib.fillText;
var convert = require('./convert');
var BADNUM = require('../../constants/numerical').BADNUM;
@@ -853,7 +853,7 @@ function calcHover(pointData, x, y, trace) {
else if(di.tx) pointData.text = di.tx;
else if(trace.text) pointData.text = trace.text;
- fillHoverText(di, trace, pointData);
+ fillText(di, trace, pointData);
Registry.getComponentMethod('errorbars', 'hoverInfo')(di, trace, pointData);
return pointData;
diff --git a/src/traces/scattermapbox/hover.js b/src/traces/scattermapbox/hover.js
index b1134016b9a..9dbe52f9775 100644
--- a/src/traces/scattermapbox/hover.js
+++ b/src/traces/scattermapbox/hover.js
@@ -12,7 +12,7 @@
var Fx = require('../../components/fx');
var Lib = require('../../lib');
var getTraceColor = require('../scatter/get_trace_color');
-var fillHoverText = require('../scatter/fill_hover_text');
+var fillText = Lib.fillText;
var BADNUM = require('../../constants/numerical').BADNUM;
module.exports = function hoverPoints(pointData, xval, yval) {
@@ -99,7 +99,7 @@ function getExtraText(trace, di, labels) {
}
if(isAll || parts.indexOf('text') !== -1) {
- fillHoverText(di, trace, text);
+ fillText(di, trace, text);
}
return text.join('
');
diff --git a/src/traces/sunburst/plot.js b/src/traces/sunburst/plot.js
index 67742e89fc8..e0792ce1aad 100644
--- a/src/traces/sunburst/plot.js
+++ b/src/traces/sunburst/plot.js
@@ -578,7 +578,10 @@ function attachFxHandlers(sliceTop, gd, cd) {
}
hoverPt.text = _cast('hovertext') || _cast('text');
- if(hasFlag('text') && hoverPt.text) thisText.push(hoverPt.text);
+ if(hasFlag('text')) {
+ var tx = hoverPt.text;
+ if(Lib.isValidTextValue(tx)) thisText.push(tx);
+ }
Fx.loneHover({
trace: traceNow,
@@ -736,7 +739,7 @@ function formatSliceLabel(pt, trace, fullLayout) {
if(hasFlag('text')) {
var tx = Lib.castOption(trace, cdi.i, 'text');
- if(tx) thisText.push(tx);
+ if(Lib.isValidTextValue(tx)) thisText.push(tx);
}
return thisText.join('
');
diff --git a/test/image/baselines/display-text_zero-number.png b/test/image/baselines/display-text_zero-number.png
new file mode 100644
index 00000000000..07fd3647934
Binary files /dev/null and b/test/image/baselines/display-text_zero-number.png differ
diff --git a/test/image/mocks/display-text_zero-number.json b/test/image/mocks/display-text_zero-number.json
new file mode 100644
index 00000000000..ebd75c52348
--- /dev/null
+++ b/test/image/mocks/display-text_zero-number.json
@@ -0,0 +1,80 @@
+{
+ "data": [
+ {
+ "name": "pie",
+ "type": "pie",
+ "labels": ["A", "B", "C", "D", "E", "F", "G"],
+ "values": [7, 6, 5, 4, 3, 2, 1],
+ "text": [null, "", "0", 0, 1, true, false],
+ "textinfo": "label+text+value",
+ "domain": {
+ "x": [0, 0.48],
+ "y": [0.52, 1]
+ }
+ },
+ {
+ "name": "sunburst",
+ "type": "sunburst",
+ "parents": ["", "A", "B", "C", "D", "E", "F"],
+ "labels": ["A", "B", "C", "D", "E", "F", "G"],
+ "values": [7, 6, 5, 4, 3, 2, 1],
+ "text": [null, "", "0", 0, 1, true, false],
+ "textinfo": "label+text+value",
+ "domain": {
+ "x": [0.52, 1],
+ "y": [0, 0.48]
+ }
+ },
+ {
+ "name": "funnel",
+ "type": "funnel",
+ "y": ["A", "B", "C", "D", "E", "F", "G"],
+ "x": [7, 6, 5, 4, 3, 2, 1],
+ "text": [null, "", "0", 0, 1, true, false],
+ "textinfo": "label+text+value"
+ },
+ {
+ "name": "waterfall",
+ "type": "waterfall",
+ "x": ["A", "B", "C", "D", "E", "F", "G"],
+ "y": [5, -4, 3, -2, 1, null, 3.14],
+ "measure": ["r", "r", "r", "r", "r", "t", "a"],
+ "text": [null, "", "0", 0, 1, true, false],
+ "textinfo": "label+text+final",
+ "textposition": "auto",
+ "xaxis": "x2",
+ "yaxis": "y2"
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 800,
+ "dragmode": "pan",
+ "xaxis": {
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "xaxis2": {
+ "anchor": "y2",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis": {
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "yaxis2": {
+ "anchor": "x2",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ }
+ }
+}
diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js
index 858bf1e51ff..7274a07de1f 100644
--- a/test/jasmine/tests/pie_test.js
+++ b/test/jasmine/tests/pie_test.js
@@ -998,6 +998,11 @@ describe('pie hovering', function() {
Lib.clearThrottle();
}
+ function _hover2() {
+ mouseEvent('mouseover', 200, 250);
+ Lib.clearThrottle();
+ }
+
function assertLabel(content, style, msg) {
assertHoverLabelContent({nums: content}, msg);
@@ -1103,6 +1108,27 @@ describe('pie hovering', function() {
.then(done);
});
+ it('should show falsy zero text', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'pie',
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
+ values: [7, 6, 5, 4, 3, 2, 1],
+ text: [null, '', '0', 0, 1, true, false],
+ textinfo: 'label+text+value'
+ }],
+ layout: {
+ width: 400,
+ height: 400
+ }
+ })
+ .then(_hover2)
+ .then(function() {
+ assertLabel('D\n0\n4\n14.3%');
+ })
+ .then(done);
+ });
+
it('should use hovertemplate if specified', function(done) {
mockCopy.data[0].name = '';
Plotly.plot(gd, mockCopy.data, mockCopy.layout)
diff --git a/test/jasmine/tests/sunburst_test.js b/test/jasmine/tests/sunburst_test.js
index 26f7c7e9a5e..bc5d423572b 100644
--- a/test/jasmine/tests/sunburst_test.js
+++ b/test/jasmine/tests/sunburst_test.js
@@ -1138,4 +1138,26 @@ describe('Test sunburst interactions edge cases', function() {
.catch(failTest)
.then(done);
});
+
+ it('should show falsy zero text', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'sunburst',
+ parents: ['', 'A', 'B', 'C', 'D', 'E', 'F'],
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
+ values: [7, 6, 5, 4, 3, 2, 1],
+ text: [null, '', '0', 0, 1, true, false],
+ textinfo: 'label+text+value'
+ }],
+ layout: {
+ width: 400,
+ height: 400
+ }
+ })
+ .then(hover(gd, 4))
+ .then(function() {
+ assertHoverLabelContent({ nums: 'D\n4\n0' });
+ })
+ .then(done);
+ });
});