diff --git a/src/components/annotations/draw.js b/src/components/annotations/draw.js index 0f793e5c56f..dfb0647e0cd 100644 --- a/src/components/annotations/draw.js +++ b/src/components/annotations/draw.js @@ -209,9 +209,13 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { var font = options.font; + var text = fullLayout.meta ? + Lib.templateString(options.text, {meta: fullLayout.meta}) : + options.text; + var annText = annTextGroupInner.append('text') .classed('annotation-text', true) - .text(options.text); + .text(text); function textLayout(s) { s.call(Drawing.font, font) @@ -678,6 +682,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { .call(textLayout) .on('edit', function(_text) { options.text = _text; + this.call(textLayout); modifyItem('text', _text); diff --git a/src/components/titles/index.js b/src/components/titles/index.js index 626570d0568..d761382ee8e 100644 --- a/src/components/titles/index.js +++ b/src/components/titles/index.js @@ -97,6 +97,10 @@ function draw(gd, titleClass, options) { if(!editable) txt = ''; } + if(fullLayout.meta) { + txt = Lib.templateString(txt, {meta: fullLayout.meta}); + } + var elShouldExist = txt || editable; if(!group) { diff --git a/src/plots/gl3d/layout/convert.js b/src/plots/gl3d/layout/convert.js index 8d8649f46a7..a065203fcb4 100644 --- a/src/plots/gl3d/layout/convert.js +++ b/src/plots/gl3d/layout/convert.js @@ -6,10 +6,10 @@ * LICENSE file in the root directory of this source tree. */ - 'use strict'; var str2RgbaArray = require('../../../lib/str2rgbarray'); +var Lib = require('../../../lib'); var AXES_NAMES = ['xaxis', 'yaxis', 'zaxis']; @@ -66,7 +66,7 @@ function AxesOptions() { var proto = AxesOptions.prototype; -proto.merge = function(sceneLayout) { +proto.merge = function(fullLayout, sceneLayout) { var opts = this; for(var i = 0; i < 3; ++i) { var axes = sceneLayout[AXES_NAMES[i]]; @@ -83,7 +83,10 @@ proto.merge = function(sceneLayout) { } // Axes labels - opts.labels[i] = axes.title.text; + opts.labels[i] = fullLayout.meta ? + Lib.templateString(axes.title.text, {meta: fullLayout.meta}) : + axes.title.text; + if('font' in axes.title) { if(axes.title.font.color) opts.labelColor[i] = str2RgbaArray(axes.title.font.color); if(axes.title.font.family) opts.labelFont[i] = axes.title.font.family; @@ -151,9 +154,9 @@ proto.merge = function(sceneLayout) { }; -function createAxesOptions(plotlyOptions) { +function createAxesOptions(fullLayout, sceneLayout) { var result = new AxesOptions(); - result.merge(plotlyOptions); + result.merge(fullLayout, sceneLayout); return result; } diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index bb3dea88c8c..a0a0e571d9c 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -305,7 +305,7 @@ function Scene(options, fullLayout) { /* * Move this to calc step? Why does it work here? */ - this.axesOptions = createAxesOptions(fullLayout[this.id]); + this.axesOptions = createAxesOptions(fullLayout, fullLayout[this.id]); this.spikeOptions = createSpikeOptions(fullLayout[this.id]); this.container = sceneContainer; this.staticMode = !!options.staticPlot; @@ -406,7 +406,7 @@ proto.plot = function(sceneData, fullLayout, layout) { this.fullSceneLayout = fullSceneLayout; this.glplotLayout = fullSceneLayout; - this.axesOptions.merge(fullSceneLayout); + this.axesOptions.merge(fullLayout, fullSceneLayout); this.spikeOptions.merge(fullSceneLayout); // Update camera and camera mode diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 7a0090b03af..521c32b7763 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -409,6 +409,17 @@ module.exports = { }, editType: 'modebar' }, + meta: { + valType: 'data_array', + 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:', + '`%{meta[i]}` where `i` is the index of the `meta`', + 'item in question.' + ].join(' ') + }, _deprecated: { title: { valType: 'string', diff --git a/src/plots/plots.js b/src/plots/plots.js index e133c617229..5e262f3ffab 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1455,6 +1455,8 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) { coerce('modebar.activecolor', Color.addOpacity(modebarDefaultColor, 0.7)); coerce('modebar.uirevision', uirevision); + coerce('meta'); + Registry.getComponentMethod( 'calendars', 'handleDefaults' diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index 8a61f7ccfd0..10524a40894 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -334,7 +334,11 @@ module.exports = function plot(gd, cdpie) { s.attr('data-notex', 1); }); - titleText.text(trace.title.text) + var txt = fullLayout.meta ? + Lib.templateString(trace.title.text, {meta: fullLayout.meta}) : + trace.title.text; + + titleText.text(txt) .attr({ 'class': 'titletext', transform: '', @@ -467,6 +471,8 @@ function determineInsideTextFont(trace, pt, layoutFont) { } function prerenderTitles(cdpie, gd) { + var fullLayout = gd._fullLayout; + var cd0, trace; // Determine the width and height of the title for each pie. for(var i = 0; i < cdpie.length; i++) { @@ -474,9 +480,13 @@ function prerenderTitles(cdpie, gd) { trace = cd0.trace; if(trace.title.text) { + var txt = fullLayout.meta ? + Lib.templateString(trace.title.text, {meta: fullLayout.meta}) : + trace.title.text; + var dummyTitle = Drawing.tester.append('text') .attr('data-notex', 1) - .text(trace.title.text) + .text(txt) .call(Drawing.font, trace.title.font) .call(svgTextUtils.convertToTspans, gd); var bBox = Drawing.bBox(dummyTitle.node(), true); diff --git a/test/image/baselines/layout_metatext.png b/test/image/baselines/layout_metatext.png new file mode 100644 index 00000000000..e8deb7bc512 Binary files /dev/null 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 new file mode 100644 index 00000000000..d24c9040219 --- /dev/null +++ b/test/image/mocks/layout_metatext.json @@ -0,0 +1,85 @@ +{ + "data": [{ + "y": [1, 2, 1] + }, { + "type": "scatterpolar", + "r": [1, 2, 1] + }, { + "type": "scatterternary", + "a": [2, 1, 1], + "b": [1, 2, 1], + "c": [1, 1, 2.12] + }, { + "type": "surface", + "z": [[1, 2, 3], [1, 2, 1], [3, 2, 1]], + "colorbar": { + "title": {"text": "Product %{meta[0]}", "side": "right"}, + "len": 0.3 + } + }, { + "type": "pie", + "labels": ["a", "b", "c"], + "values": [1, 2, 3], + "domain": {"row": 0, "column": 1}, + "title": {"text": "Employee %{meta[1]}"} + }], + "layout": { + "meta": ["A", "B", "IMPORTANT", "companyTM", 1000], + "grid": {"rows": 3, "columns": 2, "xgap": 0.2, "ygap": 0.35}, + "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]}"} + }, + "yaxis": { + "domain": {"row": 0, "column": 0}, + "title": {"text": "$ by %{meta[3]}"} + }, + "polar": { + "bgcolor": "#d3d3d3", + "domain": {"row": 1, "column": 0}, + "radialaxis": { + "title": { + "text": "%{meta[3]} -> %{meta[4]}", + "font": {"color": "red"} + } + } + }, + "ternary": { + "domain": {"row": 2, "column": 0}, + "aaxis": {"title": {"text": "%{meta[2]}"}}, + "baxis": {"title": {"text": "%{meta[1]}"}}, + "caxis": {"title": {"text": "%{meta[4]}"}} + }, + "scene": { + "domain": {"row": 1, "column": 1}, + "camera": {"eye": {"x": 0.01, "y": 0.01, "z": 2.165}}, + "xaxis": {"title": {"text": "AX:%{meta[1]}"}}, + "yaxis": {"title": {"text": "AX:%{meta[0]}"}}, + "zaxis": {"title": {"text": "%{meta[1]}%{meta[0]}"}}, + "annotations": [{ + "text": "Look at %{meta[1]}", + "bgcolor": "#d3d3d3", + "borderpad": 2, + "bordercolor": "#000", + "borderwidth": "1", + "x": 1, + "y": 1, + "z": 2 + }] + }, + + "annotations": [{ + "text": "N.B. %{meta[2]}", + "xref": "x", + "yref": "y", + "x": 1, + "y": 2 + }] + } +}