diff --git a/draftlogs/7262_add.md b/draftlogs/7262_add.md new file mode 100644 index 00000000000..850871723e2 --- /dev/null +++ b/draftlogs/7262_add.md @@ -0,0 +1,5 @@ + - Add options to allow titles to be string in various places e.g. + layout title and subtitle, colorbars, legends, shapes, + cartesian axes, gl3d axes, polar axes, ternary axes, + pie, funnelarea, carpet and indicator traces [[#7262](https://github.com/plotly/plotly.js/pull/7262)] + diff --git a/src/components/colorbar/defaults.js b/src/components/colorbar/defaults.js index 2fab3cd5209..5077a439420 100644 --- a/src/components/colorbar/defaults.js +++ b/src/components/colorbar/defaults.js @@ -123,7 +123,11 @@ module.exports = function colorbarDefaults(containerIn, containerOut, layout) { handleTickLabelDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts); handleTickMarkDefaults(colorbarIn, colorbarOut, coerce, 'linear', opts); - coerce('title.text', layout._dfltTitle.colorbar); + var titleIn = colorbarIn.title; + var titleTextDflt = (typeof titleIn === 'string') ? titleIn : + layout._dfltTitle.colorbar; + + coerce('title.text', titleTextDflt); var tickFont = colorbarOut.showticklabels ? colorbarOut.tickfont : font; diff --git a/src/components/legend/defaults.js b/src/components/legend/defaults.js index aad87454867..c6136d41818 100644 --- a/src/components/legend/defaults.js +++ b/src/components/legend/defaults.js @@ -191,7 +191,10 @@ function groupDefaults(legendId, layoutIn, layoutOut, fullData) { coerce('valign'); Lib.noneOrAll(containerIn, containerOut, ['x', 'y']); - var titleText = coerce('title.text'); + var titleIn = containerIn.title; + var titleTextDflt = (typeof titleIn === 'string') ? titleIn : undefined; + + var titleText = coerce('title.text', titleTextDflt); if(titleText) { coerce('title.side', isHorizontal ? 'left' : 'top'); var dfltTitleFont = Lib.extendFlat({}, itemFont, { diff --git a/src/components/shapes/defaults.js b/src/components/shapes/defaults.js index e725b336678..78dd10a2cc6 100644 --- a/src/components/shapes/defaults.js +++ b/src/components/shapes/defaults.js @@ -39,7 +39,10 @@ function handleShapeDefaults(shapeIn, shapeOut, fullLayout) { coerce('legend'); coerce('legendwidth'); coerce('legendgroup'); - coerce('legendgrouptitle.text'); + + var legendgrouptitleIn = shapeIn.title; + var legendgrouptitleTextDflt = (typeof titleIn === 'string') ? legendgrouptitleIn : undefined; + coerce('legendgrouptitle.text', legendgrouptitleTextDflt); Lib.coerceFont(coerce, 'legendgrouptitle.font'); coerce('legendrank'); } diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index 8c5c75d8832..b2aa14bb4a5 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -109,7 +109,9 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, // template too. var dfltFontColor = (dfltColor !== layoutAttributes.color.dflt) ? dfltColor : font.color; // try to get default title from splom trace, fallback to graph-wide value - var dfltTitle = splomStash.label || layoutOut._dfltTitle[letter]; + var titleIn = containerIn.title; + var dfltTitle = (typeof titleIn === 'string') ? titleIn : + splomStash.label || layoutOut._dfltTitle[letter]; handlePrefixSuffixDefaults(containerIn, containerOut, coerce, axType, options); if(!visible) return containerOut; diff --git a/src/plots/gl3d/layout/axis_defaults.js b/src/plots/gl3d/layout/axis_defaults.js index e3d71be24cb..54c4a4279d7 100644 --- a/src/plots/gl3d/layout/axis_defaults.js +++ b/src/plots/gl3d/layout/axis_defaults.js @@ -57,7 +57,12 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, options) { options.fullLayout); coerce('gridcolor', colorMix(containerOut.color, options.bgColor, gridLightness).toRgbString()); - coerce('title.text', axName[0]); // shouldn't this be on-par with 2D? + + var titleIn = containerIn.title; + var titleTextDflt = (typeof titleIn === 'string') ? titleIn : + axName[0]; // shouldn't this be on-par with 2D? + + coerce('title.text', titleTextDflt); containerOut.setScale = Lib.noop; diff --git a/src/plots/plots.js b/src/plots/plots.js index 6550eb353a8..bbdfe7c4dfc 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1314,6 +1314,10 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) { coerce('autotypenumbers'); + var titleIn = layoutIn.title; + var titleTextDflt = (typeof titleIn === 'string') ? titleIn : + layoutOut._dfltTitle.plot; + var font = Lib.coerceFont(coerce, 'font'); var fontSize = font.size; @@ -1321,7 +1325,7 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) { size: Math.round(fontSize * 1.4) }}); - coerce('title.text', layoutOut._dfltTitle.plot); + coerce('title.text', titleTextDflt); coerce('title.xref'); var titleYref = coerce('title.yref'); coerce('title.pad.t'); @@ -1335,7 +1339,11 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) { coerce('title.y'); coerce('title.yanchor'); - coerce('title.subtitle.text', layoutOut._dfltTitle.subtitle); + var subtitleIn = layoutIn.title; + var subtitleTextDflt = (typeof subtitleIn === 'string') ? subtitleIn : + layoutOut._dfltTitle.subtitle; + + coerce('title.subtitle.text', subtitleTextDflt); Lib.coerceFont(coerce, 'title.subtitle.font', font, { overrideDflt: { size: Math.round(layoutOut.title.font.size * 0.7) diff --git a/src/plots/ternary/layout_defaults.js b/src/plots/ternary/layout_defaults.js index 4afece35e7c..f2dcfedc24b 100644 --- a/src/plots/ternary/layout_defaults.js +++ b/src/plots/ternary/layout_defaults.js @@ -76,10 +76,13 @@ function handleAxisDefaults(containerIn, containerOut, options, ternaryLayoutOut var axName = containerOut._name; var letterUpper = axName.charAt(0).toUpperCase(); - var dfltTitle = 'Component ' + letterUpper; - var title = coerce('title.text', dfltTitle); - containerOut._hovertitle = title === dfltTitle ? title : letterUpper; + var titleIn = containerIn.title; + var dfltTitleText = (typeof titleIn === 'string') ? titleIn : + 'Component ' + letterUpper; + + var title = coerce('title.text', dfltTitleText); + containerOut._hovertitle = title === dfltTitleText ? title : letterUpper; Lib.coerceFont(coerce, 'title.font', options.font, { overrideDflt: { size: Lib.bigFont(options.font.size), diff --git a/src/traces/carpet/axis_defaults.js b/src/traces/carpet/axis_defaults.js index 1b45c87f6e5..3f0f989b030 100644 --- a/src/traces/carpet/axis_defaults.js +++ b/src/traces/carpet/axis_defaults.js @@ -107,7 +107,10 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, options) // inherit from global font color in case that was provided. var dfltFontColor = (dfltColor === containerIn.color) ? dfltColor : font.color; - var title = coerce('title.text'); + var titleIn = containerIn.title; + var titleTextDflt = (typeof titleIn === 'string') ? titleIn : undefined; + + var title = coerce('title.text', titleTextDflt); if(title) { Lib.coerceFont(coerce, 'title.font', font, { overrideDflt: { size: Lib.bigFont(font.size), diff --git a/src/traces/funnelarea/defaults.js b/src/traces/funnelarea/defaults.js index 9067c6f2542..40c86aef2bb 100644 --- a/src/traces/funnelarea/defaults.js +++ b/src/traces/funnelarea/defaults.js @@ -61,7 +61,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout handleDomainDefaults(traceOut, layout, coerce); - var title = coerce('title.text'); + var titleIn = traceIn.title; + var titleTextDflt = (typeof titleIn === 'string') ? titleIn : undefined; + + var title = coerce('title.text', titleTextDflt); if(title) { coerce('title.position'); Lib.coerceFont(coerce, 'title.font', layout.font); diff --git a/src/traces/indicator/defaults.js b/src/traces/indicator/defaults.js index 3da64f53514..4e57d796275 100644 --- a/src/traces/indicator/defaults.js +++ b/src/traces/indicator/defaults.js @@ -73,7 +73,10 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { var titleFontDflt = Lib.extendFlat({}, layout.font); titleFontDflt.size = 0.25 * (bignumberFontSize || deltaFontSize || cn.defaultNumberFontSize); Lib.coerceFont(coerce, 'title.font', titleFontDflt); - coerce('title.text'); + + var titleIn = traceIn.title; + var titleTextDflt = (typeof titleIn === 'string') ? titleIn : undefined; + coerce('title.text', titleTextDflt); // Gauge attributes var gaugeIn, gaugeOut, axisIn, axisOut; diff --git a/src/traces/pie/defaults.js b/src/traces/pie/defaults.js index f2aef62d0e2..703d1d250c7 100644 --- a/src/traces/pie/defaults.js +++ b/src/traces/pie/defaults.js @@ -118,7 +118,11 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { handleDomainDefaults(traceOut, layout, coerce); var hole = coerce('hole'); - var title = coerce('title.text'); + + var titleIn = traceIn.title; + var titleTextDflt = (typeof titleIn === 'string') ? titleIn : undefined; + + var title = coerce('title.text', titleTextDflt); if(title) { var titlePosition = coerce('title.position', hole ? 'middle center' : 'top center'); if(!hole && titlePosition === 'middle center') traceOut.title.position = 'top center'; diff --git a/test/jasmine/tests/funnelarea_test.js b/test/jasmine/tests/funnelarea_test.js index 078a29f8f9b..a7e36915f41 100644 --- a/test/jasmine/tests/funnelarea_test.js +++ b/test/jasmine/tests/funnelarea_test.js @@ -568,6 +568,24 @@ describe('Funnelarea traces', function() { .then(done, done.fail); }); + it('should be able to restyle title despite using title-as-string', function(done) { + Plotly.newPlot(gd, [{ + type: 'funnelarea', + values: [1, 2, 3], + title: 'yo', + }]) + .then(function() { + _assertTitle('base', 'yo', 'rgb(68, 68, 68)'); + return Plotly.restyle(gd, { + title: 'oy', + }); + }) + .then(function() { + _assertTitle('base', 'oy', 'rgb(68, 68, 68)'); + }) + .then(done, done.fail); + }); + it('should be able to react with new text colors', function(done) { Plotly.newPlot(gd, [{ type: 'funnelarea', diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js index d1820868f82..32c3601749b 100644 --- a/test/jasmine/tests/pie_test.js +++ b/test/jasmine/tests/pie_test.js @@ -786,6 +786,24 @@ describe('Pie traces', function() { .then(done, done.fail); }); + it('should be able to restyle title despite using title-as-string', function(done) { + Plotly.newPlot(gd, [{ + type: 'pie', + values: [1, 2, 3], + title: 'yo', + }]) + .then(function() { + _assertTitle('base', 'yo', 'rgb(68, 68, 68)'); + return Plotly.restyle(gd, { + title: 'oy', + }); + }) + .then(function() { + _assertTitle('base', 'oy', 'rgb(68, 68, 68)'); + }) + .then(done, done.fail); + }); + it('should be able to react with new text colors', function(done) { Plotly.newPlot(gd, [{ type: 'pie', diff --git a/test/jasmine/tests/plot_api_test.js b/test/jasmine/tests/plot_api_test.js index 5af80966015..6b80af5290e 100644 --- a/test/jasmine/tests/plot_api_test.js +++ b/test/jasmine/tests/plot_api_test.js @@ -526,6 +526,30 @@ describe('Test plot api', function() { .then(assertSizeAndThen(543, 432, true, 'final back to autosize')) .then(done, done.fail); }); + + it('passes update data back to plotly_relayout unmodified ' + + 'even if title-as-string has been used', function(done) { + Plotly.newPlot(gd, [{y: [1, 3, 2]}]) + .then(function() { + gd.on('plotly_relayout', function(eventData) { + expect(eventData).toEqual({ + title: 'Plotly chart', + 'xaxis.title': 'X', + 'yaxis.title': 'Y', + 'polar.radialaxis.title': 'Radial' + }); + done(); + }); + + return Plotly.relayout(gd, { + title: 'Plotly chart', + 'xaxis.title': 'X', + 'yaxis.title': 'Y', + 'polar.radialaxis.title': 'Radial' + }); + }) + .then(done, done.fail); + }); }); describe('Plotly.relayout subroutines switchboard', function() { diff --git a/test/jasmine/tests/titles_test.js b/test/jasmine/tests/titles_test.js index 839cbdd55b3..3fe447ac8d9 100644 --- a/test/jasmine/tests/titles_test.js +++ b/test/jasmine/tests/titles_test.js @@ -52,6 +52,15 @@ describe('Plot title', function() { .then(done, done.fail); }); + it('can be defined as `layout.title`', function(done) { + Plotly.newPlot(gd, data, {title: 'Plotly line chart'}) + .then(function() { + expectTitle('Plotly line chart'); + expectDefaultCenteredPosition(gd); + }) + .then(done, done.fail); + }); + it('can be updated via `relayout`', function(done) { Plotly.newPlot(gd, data, { title: { text: 'Plotly line chart' } }) .then(expectTitleFn('Plotly line chart')) @@ -869,6 +878,60 @@ describe('Title fonts can be updated', function() { } }); +describe('Titles for multiple axes', function() { + 'use strict'; + + var data = [ + {x: [1, 2, 3], y: [1, 2, 3], xaxis: 'x', yaxis: 'y'}, + {x: [1, 2, 3], y: [3, 2, 1], xaxis: 'x2', yaxis: 'y2'} + ]; + var multiAxesLayout = { + xaxis: { title: 'X-Axis 1' }, + xaxis2: { + title: 'X-Axis 2', + side: 'top' + }, + yaxis: { title: 'Y-Axis 1' }, + yaxis2: { + title: 'Y-Axis 2', + side: 'right' + } + }; + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + it('supports title-as-string', function(done) { + Plotly.newPlot(gd, data, multiAxesLayout) + .then(function() { + expect(xTitleSel(1).text()).toBe('X-Axis 1'); + expect(xTitleSel(2).text()).toBe('X-Axis 2'); + expect(yTitleSel(1).text()).toBe('Y-Axis 1'); + expect(yTitleSel(2).text()).toBe('Y-Axis 2'); + }) + .then(done, done.fail); + }); + + it('can be updated using title-as-string', function(done) { + Plotly.newPlot(gd, data, multiAxesLayout) + .then(function() { + return Plotly.relayout(gd, { + 'xaxis2.title': '2nd X-Axis', + 'yaxis2.title': '2nd Y-Axis', + }); + }) + .then(function() { + expect(xTitleSel(2).text()).toBe('2nd X-Axis'); + expect(yTitleSel(2).text()).toBe('2nd Y-Axis'); + }) + .then(done, done.fail); + }); +}); + // TODO: Add in tests for interactions with other automargined elements describe('Title automargining', function() { 'use strict';