diff --git a/draftlogs/7439_add.md b/draftlogs/7439_add.md new file mode 100644 index 00000000000..d413c949f3f --- /dev/null +++ b/draftlogs/7439_add.md @@ -0,0 +1 @@ + - Add unifiedhovertemplate to format unified hover title [[#7439](https://github.com/plotly/plotly.js/pull/7439)] diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 80fc4a07cf3..4a7677a2c43 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1211,10 +1211,26 @@ function createHoverText(hoverData, opts) { // mock legend var hoverlabel = fullLayout.hoverlabel; var font = hoverlabel.font; + + var item0 = groupedHoverData[0]; + + var unifiedhovertemplate = ( + hovermode === 'x unified' ? + item0.xa : + item0.ya + ).unifiedhovertemplate; + + var mainText = !unifiedhovertemplate ? t0 : + Lib.hovertemplateString(unifiedhovertemplate, {}, fullLayout._d3locale, + hovermode === 'x unified' ? + {xa: item0.xa, x: item0.xVal} : + {ya: item0.ya, y: item0.yVal} + ); + var mockLayoutIn = { showlegend: true, legend: { - title: {text: t0, font: font}, + title: {text: mainText, font: font}, font: font, bgcolor: hoverlabel.bgcolor, bordercolor: hoverlabel.bordercolor, diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index 4531785cd2f..a1c091863e1 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -100,7 +100,10 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, handleCategoryOrderDefaults(containerIn, containerOut, coerce, options); - if(axType !== 'category' && !options.noHover) coerce('hoverformat'); + if(!options.noHover) { + if(axType !== 'category') coerce('hoverformat'); + if(!options.noUnifiedhovertemplate) coerce('unifiedhovertemplate'); + } var dfltColor = coerce('color'); // if axis.color was provided, use it for fonts too; otherwise, diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index f1b7bbbe5d6..fc0e70e2345 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -5,6 +5,7 @@ var colorAttrs = require('../../components/color/attributes'); var dash = require('../../components/drawing/attributes').dash; var extendFlat = require('../../lib/extend').extendFlat; var templatedArray = require('../../plot_api/plot_template').templatedArray; +var templateFormatStringDescription = require('../../plots/template_attributes').templateFormatStringDescription; var descriptionWithDates = require('../../plots/cartesian/axis_format_attributes').descriptionWithDates; var ONEDAY = require('../../constants/numerical').ONEDAY; @@ -975,6 +976,15 @@ module.exports = { editType: 'none', description: descriptionWithDates('hover text') }, + unifiedhovertemplate: { + valType: 'string', + dflt: '', + editType: 'none', + description: [ + 'Template string used for rendering the title that appear on x or y unified hover box.', + templateFormatStringDescription() + ].join(' ') + }, // lines and grids showline: { valType: 'boolean', diff --git a/src/plots/gl3d/layout/axis_defaults.js b/src/plots/gl3d/layout/axis_defaults.js index e3d71be24cb..d8df3f46032 100644 --- a/src/plots/gl3d/layout/axis_defaults.js +++ b/src/plots/gl3d/layout/axis_defaults.js @@ -51,6 +51,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, options) { noTicklabelposition: true, noTicklabeloverflow: true, noInsiderange: true, + noUnifiedhovertemplate: true, bgColor: options.bgColor, calendar: options.calendar }, diff --git a/src/plots/template_attributes.js b/src/plots/template_attributes.js index 9f01f920688..5d5193c3f30 100644 --- a/src/plots/template_attributes.js +++ b/src/plots/template_attributes.js @@ -22,6 +22,7 @@ function templateFormatStringDescription(opts) { 'for details on the date formatting syntax.' ].join(' '); } +exports.templateFormatStringDescription = templateFormatStringDescription; function shapeTemplateFormatStringDescription() { return [ diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index a9af59100c0..8eea4827f54 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -6901,6 +6901,73 @@ describe('hovermode: (x|y)unified', function() { }) .then(done, done.fail); }); + + it('should format title of unified hover in respect to `unifiedhovertemplate` linear axis', function(done) { + Plotly.newPlot(gd, [{ + type: 'bar', + y: [1, 2, 3] + }, { + type: 'scatter', + y: [2, 3, 1] + }], { + xaxis: { + unifiedhovertemplate: 'X: %{x:.2f}', + }, + hovermode: 'x unified', + showlegend: false, + width: 500, + height: 500, + margin: { + t: 50, + b: 50, + l: 50, + r: 50 + } + }) + .then(function() { + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({title: 'X: 1.00', items: [ + 'trace 0 : 2', + 'trace 1 : 3' + ]}); + }) + .then(done, done.fail); + }); + + it('should format title of unified hover in respect to `unifiedhovertemplate` date axis', function(done) { + Plotly.newPlot(gd, [{ + type: 'bar', + x: ['2000-01-01', '2000-02-01', '2000-03-01'], + y: [1, 2, 3] + }, { + type: 'scatter', + x: ['2000-01-01', '2000-02-01', '2000-03-01'], + y: [2, 3, 1] + }], { + xaxis: { + type: 'date', + unifiedhovertemplate: 'X: %{x|%x %X}', + }, + hovermode: 'x unified', + showlegend: false, + width: 500, + height: 500, + margin: { + t: 50, + b: 50, + l: 50, + r: 50 + } + }) + .then(function() { + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({title: 'X: 02/01/2000 00:00:00', items: [ + 'trace 0 : 2', + 'trace 1 : 3' + ]}); + }) + .then(done, done.fail); + }); }); describe('hover on traces with (x|y)hoverformat', function() { diff --git a/test/plot-schema.json b/test/plot-schema.json index df08db9d5c3..249bcc278ba 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -14949,6 +14949,12 @@ "editType": "none", "valType": "any" }, + "unifiedhovertemplate": { + "description": "Template string used for rendering the title that appear on x or y unified hover box. Variables are inserted using %{variable}, for example \"y: %{y}\". Numbers are formatted using d3-format's syntax %{variable:d3-format}, for example \"Price: %{y:$.2f}\". https://github.com/d3/d3-format/tree/v1.4.5#d3-format for details on the formatting syntax. Dates are formatted using d3-time-format's syntax %{variable|d3-time-format}, for example \"Day: %{2019-01-01|%A}\". https://github.com/d3/d3-time-format/tree/v2.2.3#locale_format for details on the date formatting syntax.", + "dflt": "", + "editType": "none", + "valType": "string" + }, "visible": { "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false", "editType": "plot", @@ -16193,6 +16199,12 @@ "editType": "none", "valType": "any" }, + "unifiedhovertemplate": { + "description": "Template string used for rendering the title that appear on x or y unified hover box. Variables are inserted using %{variable}, for example \"y: %{y}\". Numbers are formatted using d3-format's syntax %{variable:d3-format}, for example \"Price: %{y:$.2f}\". https://github.com/d3/d3-format/tree/v1.4.5#d3-format for details on the formatting syntax. Dates are formatted using d3-time-format's syntax %{variable|d3-time-format}, for example \"Day: %{2019-01-01|%A}\". https://github.com/d3/d3-time-format/tree/v2.2.3#locale_format for details on the date formatting syntax.", + "dflt": "", + "editType": "none", + "valType": "string" + }, "visible": { "description": "A single toggle to hide the axis while preserving interaction like dragging. Default is true when a cheater plot is present on the axis, otherwise false", "editType": "plot",