diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 9c15427cd1c..5010dcd3ce0 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -931,10 +931,24 @@ function createHoverText(hoverData, opts, gd) { }); } + var xLetter = 'x'; + var yLetter = 'y'; + var zLetter = 'z'; + + if(gd._fullLayout.scene) { + xLetter = gd._fullLayout.scene.xaxis.hovertitle || xLetter; + yLetter = gd._fullLayout.scene.yaxis.hovertitle || yLetter; + zLetter = gd._fullLayout.scene.zaxis.hovertitle || zLetter; + } + else if(!gd._fullLayout.ternary) { + if(gd._fullLayout.xaxis) xLetter = gd._fullLayout.xaxis.hovertitle || xLetter; + if(gd._fullLayout.yaxis) yLetter = gd._fullLayout.yaxis.hovertitle || yLetter; + } + if(d.zLabel !== undefined) { - if(d.xLabel !== undefined) text += 'x: ' + d.xLabel + '
'; - if(d.yLabel !== undefined) text += 'y: ' + d.yLabel + '
'; - text += (text ? 'z: ' : '') + d.zLabel; + if(d.xLabel !== undefined) text += xLetter + ': ' + d.xLabel + '
'; + if(d.yLabel !== undefined) text += yLetter + ': ' + d.yLabel + '
'; + text += (text ? zLetter + ': ' : '') + d.zLabel; } else if(showCommonLabel && d[hovermode + 'Label'] === t0) { text = d[(hovermode === 'x' ? 'y' : 'x') + 'Label'] || ''; diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index 2269152de39..d1ccaa5828e 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -56,7 +56,13 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, handleCategoryOrderDefaults(containerIn, containerOut, coerce, options); - if(axType !== 'category' && !options.noHover) coerce('hoverformat'); + if(!options.noHover) { + coerce('hovertitle', letter); + + if(axType !== 'category') { + coerce('hoverformat'); + } + } if(!visible) return containerOut; diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index c0f7f9b3a83..445c95bb2d3 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -594,6 +594,14 @@ module.exports = { '*%H~%M~%S.%2f* would display *09~15~23.46*' ].join(' ') }, + hovertitle: { + valType: 'string', + role: 'info', + editType: 'none', + description: [ + 'Sets axis title to be displayed in the hovering popup' + ].join(' ') + }, // lines and grids showline: { valType: 'boolean', diff --git a/src/plots/gl3d/layout/axis_attributes.js b/src/plots/gl3d/layout/axis_attributes.js index 6699e73575a..6ff2e9a4a8c 100644 --- a/src/plots/gl3d/layout/axis_attributes.js +++ b/src/plots/gl3d/layout/axis_attributes.js @@ -109,6 +109,7 @@ module.exports = overrideAll({ tickformat: axesAttrs.tickformat, tickformatstops: axesAttrs.tickformatstops, hoverformat: axesAttrs.hoverformat, + hovertitle: axesAttrs.hovertitle, // lines and grids showline: axesAttrs.showline, linecolor: axesAttrs.linecolor, diff --git a/src/plots/polar/layout_attributes.js b/src/plots/polar/layout_attributes.js index f6905453dd9..92d4ce80cd7 100644 --- a/src/plots/polar/layout_attributes.js +++ b/src/plots/polar/layout_attributes.js @@ -118,6 +118,7 @@ var radialAxisAttrs = { // might need a 'titleside' and even 'titledirection' down the road hoverformat: axesAttrs.hoverformat, + hovertitle: axesAttrs.hovertitle, uirevision: { valType: 'any', @@ -234,6 +235,7 @@ var angularAxisAttrs = { }, hoverformat: axesAttrs.hoverformat, + hovertitle: axesAttrs.hovertitle, uirevision: { valType: 'any', diff --git a/src/plots/polar/layout_defaults.js b/src/plots/polar/layout_defaults.js index 6c7fb220dc6..4af27b19718 100644 --- a/src/plots/polar/layout_defaults.js +++ b/src/plots/polar/layout_defaults.js @@ -179,6 +179,8 @@ function handleDefaults(contIn, contOut, coerce, opts) { if(axType !== 'category') coerceAxis('hoverformat'); + coerceAxis('hovertitle'); + axOut._input = axIn; } diff --git a/src/plots/ternary/layout_attributes.js b/src/plots/ternary/layout_attributes.js index effa824be8d..13948d130cb 100644 --- a/src/plots/ternary/layout_attributes.js +++ b/src/plots/ternary/layout_attributes.js @@ -42,6 +42,7 @@ var ternaryAxesAttrs = { tickformat: axesAttrs.tickformat, tickformatstops: axesAttrs.tickformatstops, hoverformat: axesAttrs.hoverformat, + hovertitle: axesAttrs.hovertitle, // lines and grids showline: extendFlat({}, axesAttrs.showline, {dflt: true}), linecolor: axesAttrs.linecolor, diff --git a/src/plots/ternary/layout_defaults.js b/src/plots/ternary/layout_defaults.js index 7ab47dee7ac..07400a67a25 100644 --- a/src/plots/ternary/layout_defaults.js +++ b/src/plots/ternary/layout_defaults.js @@ -86,7 +86,7 @@ function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut var dfltTitle = 'Component ' + letterUpper; var title = coerce('title.text', dfltTitle); - containerOut._hovertitle = title === dfltTitle ? title : letterUpper; + coerce('hovertitle', title === dfltTitle ? title : letterUpper); Lib.coerceFont(coerce, 'title.font', { family: options.font.family, @@ -126,5 +126,6 @@ function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut }); coerce('hoverformat'); + coerce('hovertitle'); coerce('layer'); } diff --git a/src/traces/carpet/axis_attributes.js b/src/traces/carpet/axis_attributes.js index 589c9dee56b..47f53aea561 100644 --- a/src/traces/carpet/axis_attributes.js +++ b/src/traces/carpet/axis_attributes.js @@ -68,6 +68,14 @@ module.exports = { }, editType: 'calc', }, + hovertitle: { + valType: 'string', + role: 'info', + editType: 'none', + description: [ + 'Sets axis title to be displayed in the hovering popup' + ].join(' ') + }, type: { valType: 'enumerated', // '-' means we haven't yet run autotype or couldn't find any data diff --git a/src/traces/carpet/axis_defaults.js b/src/traces/carpet/axis_defaults.js index a14341c2943..7cc184c3bb3 100644 --- a/src/traces/carpet/axis_defaults.js +++ b/src/traces/carpet/axis_defaults.js @@ -95,8 +95,7 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, options) coerce('labelpadding'); - containerOut._hovertitle = letter; - + coerce('hovertitle', letter); if(axType === 'date') { var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults'); diff --git a/src/traces/scattercarpet/hover.js b/src/traces/scattercarpet/hover.js index f8b47447a39..c9d9364ecb6 100644 --- a/src/traces/scattercarpet/hover.js +++ b/src/traces/scattercarpet/hover.js @@ -58,7 +58,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { if(ax.labelprefix && ax.labelprefix.length > 0) { prefix = ax.labelprefix.replace(/ = $/, ''); } else { - prefix = ax._hovertitle; + prefix = ax.hovertitle; } text.push(prefix + ': ' + val.toFixed(3) + ax.labelsuffix); diff --git a/src/traces/scatterpolar/hover.js b/src/traces/scatterpolar/hover.js index ea52df6b1ee..c6e66bc0676 100644 --- a/src/traces/scatterpolar/hover.js +++ b/src/traces/scatterpolar/hover.js @@ -40,13 +40,11 @@ function makeHoverPointText(cdi, trace, subplot, pointData) { var radialAxis = subplot.radialAxis; var angularAxis = subplot.angularAxis; - radialAxis._hovertitle = 'r'; - angularAxis._hovertitle = 'θ'; var hoverinfo = cdi.hi || trace.hoverinfo; var text = []; - function textPart(ax, val) { - text.push(ax._hovertitle + ': ' + Axes.tickText(ax, val, 'hover').text); + function textPart(dfltText, ax, val) { + text.push((ax.hovertitle || dfltText) + ': ' + Axes.tickText(ax, val, 'hover').text); } if(!trace.hovertemplate) { @@ -54,11 +52,12 @@ function makeHoverPointText(cdi, trace, subplot, pointData) { if(parts.indexOf('all') !== -1) parts = ['r', 'theta', 'text']; if(parts.indexOf('r') !== -1) { - textPart(radialAxis, radialAxis.c2l(cdi.r)); + textPart('r', radialAxis, radialAxis.c2l(cdi.r)); } if(parts.indexOf('theta') !== -1) { var theta = cdi.theta; textPart( + 'θ', angularAxis, angularAxis.thetaunit === 'degrees' ? Lib.rad2deg(theta) : theta ); diff --git a/src/traces/scatterternary/hover.js b/src/traces/scatterternary/hover.js index eaf0ad26421..30e4722d2ce 100644 --- a/src/traces/scatterternary/hover.js +++ b/src/traces/scatterternary/hover.js @@ -54,7 +54,8 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { var hoverinfo = cdi.hi || trace.hoverinfo; var text = []; function textPart(ax, val) { - text.push(ax._hovertitle + ': ' + Axes.tickText(ax, val, 'hover').text); + var axTitle = ax.hovertitle || ax.title.text; + text.push(axTitle + ': ' + Axes.tickText(ax, val, 'hover').text); } if(!trace.hovertemplate) { var parts = hoverinfo.split('+'); diff --git a/test/image/baselines/gl3d_surface_after_heatmap.png b/test/image/baselines/gl3d_surface_after_heatmap.png index e2e84ff714a..06bd02e3df2 100644 Binary files a/test/image/baselines/gl3d_surface_after_heatmap.png and b/test/image/baselines/gl3d_surface_after_heatmap.png differ diff --git a/test/image/mocks/gl3d_ibm-plot.json b/test/image/mocks/gl3d_ibm-plot.json index 166e16463e4..f15a887c8bc 100644 --- a/test/image/mocks/gl3d_ibm-plot.json +++ b/test/image/mocks/gl3d_ibm-plot.json @@ -304,6 +304,7 @@ "scene": { "xaxis": { "title": "Term", + "hovertitle": "Term", "titlefont": { "color": "#D9D9D9" }, @@ -318,6 +319,7 @@ }, "yaxis": { "title": "Moneyness", + "hovertitle": "Moneyness", "titlefont": { "color": "#D9D9D9" }, @@ -332,6 +334,7 @@ }, "zaxis": { "title": "Volatilty", + "hovertitle": "Volatilty", "titlefont": { "color": "#D9D9D9" }, diff --git a/test/image/mocks/gl3d_surface_after_heatmap.json b/test/image/mocks/gl3d_surface_after_heatmap.json index bd34b7f8c70..da6b4a4cf37 100644 --- a/test/image/mocks/gl3d_surface_after_heatmap.json +++ b/test/image/mocks/gl3d_surface_after_heatmap.json @@ -2,28 +2,72 @@ "data": [ { "type": "heatmap", - "x": [0, 1, 2], - "y": [0, 1, 2], + "zsmooth": "best", + "x": [0, 1, 2, 3, 4], + "y": [0, 1, 2, 3, 4], "z": [ - [0, 1, 0], - [1, 0, 1], - [0, 1, 0] + [0, 1, 0, 1, 0], + [1, 0.25, 0.75, 0.25, 1], + [0, 0.75, 0.25, 0.75, 0], + [1, 0.25, 0.75, 0.25, 1], + [0, 1, 0, 1, 0] ] }, { "type": "surface", - "x": [0, 1, 2], - "y": [0, 1, 2], + "x": [0, 1, 2, 3, 4], + "y": [0, 1, 2, 3, 4], "z": [ - [0, 1, 0], - [1, 0, 1], - [0, 1, 0] + [0, 1, 0, 1, 0], + [1, 0.25, 0.75, 0.25, 1], + [0, 0.75, 0.25, 0.75, 0], + [1, 0.25, 0.75, 0.25, 1], + [0, 1, 0, 1, 0] ] } ], "layout": { "title": "Surface 3d plot on top of 2d heatmap!", "width": 600, - "height": 400 + "height": 400, + "xaxis": { + "hovertitle": "lon", + "title": { + "text": "Longitude" + } + }, + "yaxis": { + "hovertitle": "lat", + "title": { + "text": "Latitude" + } + }, + "scene": { + "xaxis": { + "hovertitle": "lon", + "title": { + "text": "Longitude" + } + }, + "yaxis": { + "hovertitle": "lat", + "title": { + "text": "Latitude" + } + }, + "zaxis": { + "hovertitle": "alt", + "title": { + "text": "Altitude" + } + }, + "camera": { + "eye": { + "x": 2, + "y": 2, + "z": 2 + } + } + } } } diff --git a/test/image/mocks/polar_wind-rose.json b/test/image/mocks/polar_wind-rose.json index fb40daa5592..3edd02cad3a 100644 --- a/test/image/mocks/polar_wind-rose.json +++ b/test/image/mocks/polar_wind-rose.json @@ -31,8 +31,8 @@ "polar": { "barmode": "overlay", "bargap": 0, - "radialaxis": {"ticksuffix": "%", "angle": 45, "dtick": 20}, - "angularaxis": {"direction": "clockwise"} + "radialaxis": {"hovertitle": "distribution", "ticksuffix": "%", "angle": 45, "dtick": 20}, + "angularaxis": {"hovertitle": "direction", "direction": "clockwise"} } } } diff --git a/test/jasmine/tests/ternary_test.js b/test/jasmine/tests/ternary_test.js index f6475dbab1a..9fdae114349 100644 --- a/test/jasmine/tests/ternary_test.js +++ b/test/jasmine/tests/ternary_test.js @@ -131,7 +131,7 @@ describe('ternary plots', function() { check([ 'Component A: 0.5', - 'B: 0.25', + 'chocolate: 0.25', 'Component C: 0.25' ].join('\n'), { bgcolor: 'rgb(31, 119, 180)', @@ -148,7 +148,7 @@ describe('ternary plots', function() { .then(function() { check([ 'Component A: 0.5', - 'B: 0.25', + 'chocolate: 0.25', 'Component C: 0.25' ].join('\n'), { bgcolor: 'rgb(31, 119, 180)',