diff --git a/src/traces/mesh3d/attributes.js b/src/traces/mesh3d/attributes.js index 86125b522b7..2c6db0d1450 100644 --- a/src/traces/mesh3d/attributes.js +++ b/src/traces/mesh3d/attributes.js @@ -12,10 +12,10 @@ var colorAttrs = require('../../components/colorscale/color_attributes'); var colorscaleAttrs = require('../../components/colorscale/attributes'); var colorbarAttrs = require('../../components/colorbar/attributes'); var surfaceAtts = require('../surface/attributes'); +var baseAttrs = require('../../plots/attributes'); var extendFlat = require('../../lib/extend').extendFlat; - module.exports = extendFlat(colorAttrs('', 'calc', false), { x: { valType: 'data_array', @@ -78,6 +78,19 @@ module.exports = extendFlat(colorAttrs('', 'calc', false), { }, + text: { + valType: 'string', + role: 'info', + dflt: '', + arrayOk: true, + editType: 'calc', + description: [ + 'Sets the text elements associated with the vertices.', + 'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,', + 'these elements will be seen in the hover labels.' + ].join(' ') + }, + delaunayaxis: { valType: 'enumerated', role: 'info', @@ -209,5 +222,7 @@ module.exports = extendFlat(colorAttrs('', 'calc', false), { description: 'Epsilon for face normals calculation avoids math issues arising from degenerate geometry.' }, editType: 'calc' - }, surfaceAtts.lighting) + }, surfaceAtts.lighting), + + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {editType: 'calc'}) }); diff --git a/src/traces/mesh3d/convert.js b/src/traces/mesh3d/convert.js index 31ebf3c30c3..b32910e3f49 100644 --- a/src/traces/mesh3d/convert.js +++ b/src/traces/mesh3d/convert.js @@ -40,6 +40,13 @@ proto.handlePick = function(selection) { this.data.z[selectIndex] ]; + var text = this.data.text; + if(Array.isArray(text) && text[selectIndex] !== undefined) { + selection.textLabel = text[selectIndex]; + } else if(text) { + selection.textLabel = text; + } + return true; } }; diff --git a/src/traces/mesh3d/defaults.js b/src/traces/mesh3d/defaults.js index 2e9df0d3b1c..4049e5f8b5f 100644 --- a/src/traces/mesh3d/defaults.js +++ b/src/traces/mesh3d/defaults.js @@ -84,4 +84,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout else if('vertexcolor' in traceIn) coerce('vertexcolor'); else coerce('color', defaultColor); } + + coerce('text'); }; diff --git a/src/traces/scatter3d/attributes.js b/src/traces/scatter3d/attributes.js index 229f51e1c5f..1f814195485 100644 --- a/src/traces/scatter3d/attributes.js +++ b/src/traces/scatter3d/attributes.js @@ -11,6 +11,7 @@ var scatterAttrs = require('../scatter/attributes'); var colorAttributes = require('../../components/colorscale/color_attributes'); var errorBarAttrs = require('../../components/errorbars/attributes'); +var baseAttrs = require('../../plots/attributes'); var DASHES = require('../../constants/gl3d_dashes'); var MARKER_SYMBOLS = require('../../constants/gl3d_markers'); @@ -171,6 +172,8 @@ var attrs = module.exports = overrideAll({ error_x: errorBarAttrs, error_y: errorBarAttrs, error_z: errorBarAttrs, + + hoverinfo: extendFlat({}, baseAttrs.hoverinfo) }, 'calc', 'nested'); attrs.x.editType = attrs.y.editType = attrs.z.editType = 'calc+clearAxisTypes'; diff --git a/src/traces/surface/attributes.js b/src/traces/surface/attributes.js index 896988070b4..2ae56bad8db 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 baseAttrs = require('../../plots/attributes'); var extendFlat = require('../../lib/extend').extendFlat; var overrideAll = require('../../plot_api/edit_types').overrideAll; @@ -112,9 +113,17 @@ var attrs = module.exports = overrideAll({ }, text: { - valType: 'data_array', - description: 'Sets the text elements associated with each z value.' + valType: 'string', + role: 'info', + dflt: '', + arrayOk: true, + description: [ + 'Sets the text elements associated with each z value.', + 'If trace `hoverinfo` contains a *text* flag and *hovertext* is not set,', + 'these elements will be seen in the hover labels.' + ].join(' ') }, + surfacecolor: { valType: 'data_array', description: [ @@ -242,7 +251,9 @@ var attrs = module.exports = overrideAll({ zmax: extendFlat({}, colorscaleAttrs.zmax, { description: 'Obsolete. Use `cmax` instead.' }) - } + }, + + hoverinfo: extendFlat({}, baseAttrs.hoverinfo) }, 'calc', 'nested'); attrs.x.editType = attrs.y.editType = attrs.z.editType = 'calc+clearAxisTypes'; diff --git a/src/traces/surface/convert.js b/src/traces/surface/convert.js index 029369206bd..1881235686e 100644 --- a/src/traces/surface/convert.js +++ b/src/traces/surface/convert.js @@ -67,10 +67,13 @@ proto.handlePick = function(selection) { ]; var text = this.data.text; - if(text && text[selectIndex[1]] && text[selectIndex[1]][selectIndex[0]] !== undefined) { + if(Array.isArray(text) && text[selectIndex[1]] && text[selectIndex[1]][selectIndex[0]] !== undefined) { selection.textLabel = text[selectIndex[1]][selectIndex[0]]; + } else if(text) { + selection.textLabel = text; + } else { + selection.textLabel = ''; } - else selection.textLabel = ''; selection.data.dataCoordinate = selection.dataCoordinate.slice(); diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js index d6fc3a2e145..d1df6a2709a 100644 --- a/test/jasmine/tests/gl3d_plot_interact_test.js +++ b/test/jasmine/tests/gl3d_plot_interact_test.js @@ -36,7 +36,10 @@ describe('Test gl3d plots', function() { var mock3 = require('@mocks/gl3d_autocolorscale'); function assertHoverText(xLabel, yLabel, zLabel, textLabel) { - var content = [xLabel, yLabel, zLabel]; + var content = []; + if(xLabel) content.push(xLabel); + if(yLabel) content.push(yLabel); + if(zLabel) content.push(zLabel); if(textLabel) content.push(textLabel); assertHoverLabelContent({nums: content.join('\n')}); } @@ -193,6 +196,16 @@ describe('Test gl3d plots', function() { .then(_hover) .then(function() { assertHoverText('x: 二 6, 2017', 'y: c', 'z: 100k', 'Clementine'); + + return Plotly.restyle(gd, 'hoverinfo', 'text'); + }) + .then(function() { + assertHoverText(null, null, null, 'Clementine'); + + return Plotly.restyle(gd, 'hoverinfo', 'z'); + }) + .then(function() { + assertHoverText(null, null, '100k'); }) .catch(fail) .then(done); @@ -273,6 +286,23 @@ describe('Test gl3d plots', function() { 'colorbar.tickvals': undefined, 'colorbar.ticktext': undefined }); + + return Plotly.restyle(gd, 'hoverinfo', 'z'); + }) + .then(_hover) + .then(function() { + assertHoverText(null, null, '43'); + + return Plotly.restyle(gd, 'hoverinfo', 'text'); + }) + .then(_hover) + .then(function() { + assertHoverText(null, null, null, 'one two'); + + return Plotly.restyle(gd, 'text', 'yo!'); + }) + .then(function() { + assertHoverText(null, null, null, 'yo!'); }) .then(done); }); @@ -302,6 +332,60 @@ describe('Test gl3d plots', function() { .then(done); }); + it('should display correct hover labels (mesh3d case)', function(done) { + var x = [1, 1, 2, 3, 4, 2]; + var y = [2, 1, 3, 4, 5, 3]; + var z = [3, 7, 4, 5, 3.5, 2]; + var text = x.map(function(_, i) { + return [ + 'ts: ' + x[i], + 'hz: ' + y[i], + 'ftt:' + z[i] + ].join('
'); + }); + + function _hover() { + mouseEvent('mouseover', 250, 250); + return delay(20)(); + } + + Plotly.newPlot(gd, [{ + type: 'mesh3d', + x: x, + y: y, + z: z, + text: text + }], { + width: 500, + height: 500 + }) + .then(delay(20)) + .then(_hover) + .then(function() { + assertHoverText('x: 3', 'y: 4', 'z: 5', 'ts: 3\nhz: 4\nftt:5'); + }) + .then(function() { + return Plotly.restyle(gd, 'hoverinfo', 'x+y'); + }) + .then(function() { + assertHoverText('(3, 4)'); + }) + .then(function() { + return Plotly.restyle(gd, 'hoverinfo', 'text'); + }) + .then(function() { + assertHoverText('ts: 3\nhz: 4\nftt:5'); + }) + .then(function() { + return Plotly.restyle(gd, 'text', 'yo!'); + }) + .then(function() { + assertHoverText(null, null, null, 'yo!'); + }) + .catch(fail) + .then(done); + }); + it('should be able to reversibly change trace type', function(done) { var _mock = Lib.extendDeep({}, mock2); var sceneLayout = { aspectratio: { x: 1, y: 1, z: 1 } };