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); + }); });