diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 9c15427cd1c..50594ac4502 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -738,6 +738,7 @@ function _hover(gd, evt, subplot, noHoverEvent) { var EXTRA_STRING_REGEX = /([\s\S]*)<\/extra>/; function createHoverText(hoverData, opts, gd) { + var fullLayout = gd._fullLayout; var hovermode = opts.hovermode; var rotateLabels = opts.rotateLabels; var bgColor = opts.bgColor; @@ -925,6 +926,10 @@ function createHoverText(hoverData, opts, gd) { if(d.nameOverride !== undefined) d.name = d.nameOverride; if(d.name) { + if(fullLayout.meta) { + d.name = Lib.templateString(d.name, {meta: fullLayout.meta}); + } + name = svgTextUtils.plainText(d.name || '', { len: d.nameLength, allowedTags: ['br', 'sub', 'sup', 'b', 'i', 'em'] @@ -970,7 +975,12 @@ function createHoverText(hoverData, opts, gd) { var hovertemplateLabels = d.hovertemplateLabels || d; var eventData = d.eventData[0] || {}; if(hovertemplate) { - text = Lib.hovertemplateString(hovertemplate, hovertemplateLabels, eventData); + text = Lib.hovertemplateString( + hovertemplate, + hovertemplateLabels, + eventData, + {meta: fullLayout.meta} + ); text = text.replace(EXTRA_STRING_REGEX, function(match, extra) { name = extra; // Assign name for secondary text label diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 58dfb61243d..6c1baf89ce5 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -404,9 +404,13 @@ function drawTexts(g, gd, maxLength) { var trace = legendItem.trace; var isPie = Registry.traceIs(trace, 'pie'); var traceIndex = trace.index; - var name = isPie ? legendItem.label : trace.name; var isEditable = gd._context.edits.legendText && !isPie; + var name = isPie ? legendItem.label : trace.name; + if(fullLayout.meta) { + name = Lib.templateString(name, {meta: fullLayout.meta}); + } + var textEl = Lib.ensureSingle(g, 'text', 'legendtext'); textEl.attr('text-anchor', 'start') diff --git a/src/components/rangeselector/draw.js b/src/components/rangeselector/draw.js index 5674dbd5d45..7c7ea766287 100644 --- a/src/components/rangeselector/draw.js +++ b/src/components/rangeselector/draw.js @@ -26,7 +26,6 @@ var FROM_BR = alignmentConstants.FROM_BR; var constants = require('./constants'); var getUpdateObject = require('./get_update_object'); - module.exports = function draw(gd) { var fullLayout = gd._fullLayout; @@ -152,12 +151,16 @@ function drawButtonText(button, selectorLayout, d, gd) { }); text.call(Drawing.font, selectorLayout.font) - .text(getLabel(d)) + .text(getLabel(d, gd._fullLayout.meta)) .call(textLayout); } -function getLabel(opts) { - if(opts.label) return opts.label; +function getLabel(opts, meta) { + if(opts.label) { + return meta ? + Lib.templateString(opts.label, {meta: meta}) : + opts.label; + } if(opts.step === 'all') return 'all'; diff --git a/src/components/sliders/draw.js b/src/components/sliders/draw.js index 9db807ba1ce..50342bfa523 100644 --- a/src/components/sliders/draw.js +++ b/src/components/sliders/draw.js @@ -317,6 +317,10 @@ function drawCurrentValue(sliderGroup, sliderOpts, valueOverride) { str += valueOverride; } else { var curVal = sliderOpts.steps[sliderOpts.active].label; + var meta = sliderOpts._gd._fullLayout.meta; + if(meta) { + curVal = Lib.templateString(curVal, {meta: meta}); + } str += curVal; } @@ -364,8 +368,14 @@ function drawLabel(item, data, sliderOpts) { }); }); + var tx = data.step.label; + var meta = sliderOpts._gd._fullLayout.meta; + if(meta) { + tx = Lib.templateString(tx, {meta: meta}); + } + text.call(Drawing.font, sliderOpts.font) - .text(data.step.label) + .text(tx) .call(svgTextUtils.convertToTspans, sliderOpts._gd); return text; diff --git a/src/components/updatemenus/draw.js b/src/components/updatemenus/draw.js index 0d005ee18b5..197472c99fe 100644 --- a/src/components/updatemenus/draw.js +++ b/src/components/updatemenus/draw.js @@ -442,8 +442,14 @@ function drawItemText(item, menuOpts, itemOpts, gd) { }); }); + var tx = itemOpts.label; + var meta = gd._fullLayout.meta; + if(meta) { + tx = Lib.templateString(tx, {meta: meta}); + } + text.call(Drawing.font, menuOpts.font) - .text(itemOpts.label) + .text(tx) .call(svgTextUtils.convertToTspans, gd); } diff --git a/src/lib/index.js b/src/lib/index.js index a28a4a31a4f..1c0c0eb7900 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -994,7 +994,7 @@ lib.numSeparate = function(value, separators, separatethousands) { return x1 + x2; }; -var TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)(:[^}]*)?}/g; +lib.TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)(:[^}]*)?}/g; var SIMPLE_PROPERTY_REGEX = /^\w*$/; /** @@ -1014,7 +1014,7 @@ lib.templateString = function(string, obj) { // just in case it speeds things up *slightly*: var getterCache = {}; - return string.replace(TEMPLATE_STRING_REGEX, function(dummy, key) { + return string.replace(lib.TEMPLATE_STRING_REGEX, function(dummy, key) { if(SIMPLE_PROPERTY_REGEX.test(key)) { return obj[key] || ''; } @@ -1047,7 +1047,7 @@ lib.hovertemplateString = function(string, labels) { // just in case it speeds things up *slightly*: var getterCache = {}; - return string.replace(TEMPLATE_STRING_REGEX, function(match, key, format) { + return string.replace(lib.TEMPLATE_STRING_REGEX, function(match, key, format) { var obj, value, i; for(i = 2; i < args.length; i++) { obj = args[i]; diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index a86eea3eb8c..ac204dbe8c6 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -416,8 +416,9 @@ module.exports = { editType: 'plot', description: [ 'Assigns extra meta information that can be used in various `text` attributes.', - 'Attributes such as the graph, axis and colorbar `title.text` and annotation `text`', - 'support `meta`. One can access `meta` fields using template strings:', + 'Attributes such as the graph, axis and colorbar `title.text`, annotation `text`', + '`trace.name` in legend items, `rangeselector`, `updatemenues` and `sliders` `label` text', + 'all support `meta`. One can access `meta` fields using template strings:', '`%{meta[i]}` where `i` is the index of the `meta`', 'item in question.' ].join(' ') diff --git a/test/image/baselines/layout_metatext.png b/test/image/baselines/layout_metatext.png index d36837bec6f..88358b96a9f 100644 Binary files a/test/image/baselines/layout_metatext.png and b/test/image/baselines/layout_metatext.png differ diff --git a/test/image/mocks/layout_metatext.json b/test/image/mocks/layout_metatext.json index d24c9040219..787f134862c 100644 --- a/test/image/mocks/layout_metatext.json +++ b/test/image/mocks/layout_metatext.json @@ -1,6 +1,8 @@ { "data": [{ - "y": [1, 2, 1] + "x": ["2019-01-01", "2019-02-10", "2019-03-24"], + "y": [1, 2, 1], + "name": "TRACE %{meta[3]}" }, { "type": "scatterpolar", "r": [1, 2, 1] @@ -29,12 +31,20 @@ "width": 700, "height": 800, "margin": {"b": 40}, - "showlegend": false, "title": {"text": "This graph is %{meta[2]}"}, "xaxis": { "domain": {"row": 0, "column": 0}, - "title": {"text": "Worth more than %{meta[4]} %{meta[1]}"} + "title": {"text": "Worth more than %{meta[4]} %{meta[1]}"}, + "rangeselector": { + "buttons": [{ + "step": "all" + }, { + "step": "month", + "count": 2, + "label": "backup %{meta[2]}" + }] + } }, "yaxis": { "domain": {"row": 0, "column": 0}, @@ -78,8 +88,30 @@ "text": "N.B. %{meta[2]}", "xref": "x", "yref": "y", - "x": 1, + "x": "2019-02-10", "y": 2 + }], + + "updatemenus": [{ + "buttons": [{ + "label": "Btn-%{meta[0]}", + "method": "restyle", + "args": ["visible", false] + }], + "y": 1, + "yanchor": "bottom" + }], + + "sliders": [{ + "steps": [{ + "label": "step-%{meta[0]}", + "method": "restyle", + "args": ["marker.color", "red"] + }, { + "label": "step-%{meta[1]}", + "method": "restyle", + "args": ["marker.color", "blue"] + }] }] } } diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index 5d025b8c1d8..d213c898e80 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -1733,6 +1733,51 @@ describe('hover info', function() { .catch(failTest) .then(done); }); + + it('should work with layout.meta references', function(done) { + var gd = document.getElementById('graph'); + + Plotly.update(gd, + {hovertemplate: 'TRACE -- %{meta[0]}%{meta[1]}'}, + {meta: ['A', '$$$']} + ).then(function() { + Fx.hover('graph', evt, 'xy'); + + assertHoverLabelContent({ + nums: 'TRACE -- A', + name: '$$$', + axis: '0.388' + }); + }) + .catch(failTest) + .then(done); + }); + }); + + it('should work with trace.name linked to layout.meta', function(done) { + var gd = createGraphDiv(); + + Plotly.plot(gd, [{ + y: [1, 1, 1], + name: '%{meta[0]}', + marker: {size: 40} + }, { + y: [1] + }], { + meta: ['yo!'], + width: 400, + height: 400 + }) + .then(function() { _hoverNatural(gd, 200, 200); }) + .then(function() { + assertHoverLabelContent({ + nums: '1', + name: 'yo!', + axis: '2' + }); + }) + .catch(failTest) + .then(done); }); });