diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index 98c55bec6ad..1e92577c607 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -193,5 +193,38 @@ module.exports = { 'Sets the vertical alignment of the symbols with respect to their associated text.', ].join(' ') }, + title: { + text: { + valType: 'string', + dflt: '', + role: 'info', + editType: 'legend', + description: [ + 'Sets the title of the legend.' + ].join(' ') + }, + font: fontAttrs({ + editType: 'legend', + description: [ + 'Sets this legend\'s title font.' + ].join(' '), + }), + side: { + valType: 'enumerated', + values: ['top', 'left', 'top left'], + role: 'style', + editType: 'legend', + description: [ + 'Determines the location of legend\'s title', + 'with respect to the legend items.', + 'Defaulted to *top* with `orientation` is *h*.', + 'Defaulted to *left* with `orientation` is *v*.', + 'The *top left* options could be used to expand', + 'legend area in both x and y sides.' + ].join(' ') + }, + editType: 'legend', + }, + editType: 'legend' }; diff --git a/src/components/legend/constants.js b/src/components/legend/constants.js index a4fa79dd92a..449360911ba 100644 --- a/src/components/legend/constants.js +++ b/src/components/legend/constants.js @@ -15,6 +15,8 @@ module.exports = { scrollBarMargin: 4, scrollBarEnterAttrs: {rx: 20, ry: 3, width: 0, height: 0}, + // number of px between legend title and (left) side of legend (always in x direction and from inner border) + titlePad: 2, // number of px between legend symbol and legend text (always in x direction) textGap: 40, // number of px between each legend item (x and/or y direction) diff --git a/src/components/legend/defaults.js b/src/components/legend/defaults.js index f73add5cba4..ab32e638bbf 100644 --- a/src/components/legend/defaults.js +++ b/src/components/legend/defaults.js @@ -33,7 +33,14 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { // *would* be shown by default, toward the two traces you need to // ensure the legend is shown by default, because this can still help // disambiguate. - if(trace.showlegend || trace._dfltShowLegend) { + if(trace.showlegend || ( + trace._dfltShowLegend && !( + trace._module && + trace._module.attributes && + trace._module.attributes.showlegend && + trace._module.attributes.showlegend.dflt === false + ) + )) { legendTraceCount++; if(trace.showlegend) { legendReallyHasATrace = true; @@ -116,4 +123,10 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { coerce('yanchor', defaultYAnchor); coerce('valign'); Lib.noneOrAll(containerIn, containerOut, ['x', 'y']); + + var titleText = coerce('title.text'); + if(titleText) { + coerce('title.side', orientation === 'h' ? 'left' : 'top'); + Lib.coerceFont(coerce, 'title.font', layoutOut.font); + } }; diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 205eb43ea9e..3a9d5f942ef 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -65,6 +65,19 @@ module.exports = function draw(gd) { var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox'); + var title = opts.title; + opts._titleWidth = 0; + opts._titleHeight = 0; + if(title.text) { + var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext'); + titleEl.attr('text-anchor', 'start') + .classed('user-select-none', true) + .call(Drawing.font, title.font) + .text(title.text); + + textLayout(titleEl, scrollBox, gd); // handle mathjax or multi-line text and compute title height + } + var scrollBar = Lib.ensureSingle(legend, 'rect', 'scrollbar', function(s) { s.attr(constants.scrollBarEnterAttrs) .call(Color.fill, constants.scrollBarColor); @@ -121,7 +134,7 @@ module.exports = function draw(gd) { } // Set size and position of all the elements that make up a legend: - // legend, background and border, scroll box and scroll bar + // legend, background and border, scroll box and scroll bar as well as title Drawing.setTranslate(legend, lx, ly); // to be safe, remove previous listeners @@ -370,23 +383,17 @@ function drawTexts(g, gd) { textEl.attr('text-anchor', 'start') .classed('user-select-none', true) - .call(Drawing.font, fullLayout.legend.font) + .call(Drawing.font, opts.font) .text(isEditable ? ensureLength(name, maxNameLength) : name); svgTextUtils.positionText(textEl, constants.textGap, 0); - function textLayout(s) { - svgTextUtils.convertToTspans(s, gd, function() { - computeTextDimensions(g, gd); - }); - } - if(isEditable) { textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name}) - .call(textLayout) + .call(textLayout, g, gd) .on('edit', function(newName) { this.text(ensureLength(newName, maxNameLength)) - .call(textLayout); + .call(textLayout, g, gd); var fullInput = legendItem.trace._fullInput || {}; var update = {}; @@ -407,7 +414,7 @@ function drawTexts(g, gd) { return Registry.call('_guiRestyle', gd, update, traceIndex); }); } else { - textLayout(textEl); + textLayout(textEl, g, gd); } } @@ -460,18 +467,24 @@ function setupTraceToggle(g, gd) { }); } +function textLayout(s, g, gd) { + svgTextUtils.convertToTspans(s, gd, function() { + computeTextDimensions(g, gd); + }); +} + function computeTextDimensions(g, gd) { var legendItem = g.data()[0][0]; - - if(!legendItem.trace.showlegend) { + if(legendItem && !legendItem.trace.showlegend) { g.remove(); return; } var mathjaxGroup = g.select('g[class*=math-group]'); var mathjaxNode = mathjaxGroup.node(); + var bw = gd._fullLayout.legend.borderwidth; var opts = gd._fullLayout.legend; - var lineHeight = opts.font.size * LINE_SPACING; + var lineHeight = (legendItem ? opts : opts.title).font.size * LINE_SPACING; var height, width; if(mathjaxNode) { @@ -480,24 +493,56 @@ function computeTextDimensions(g, gd) { height = mathjaxBB.height; width = mathjaxBB.width; - Drawing.setTranslate(mathjaxGroup, 0, (height / 4)); + if(legendItem) { + Drawing.setTranslate(mathjaxGroup, 0, height * 0.25); + } else { // case of title + Drawing.setTranslate(mathjaxGroup, bw, height * 0.75 + bw); + } } else { - var text = g.select('.legendtext'); - var textLines = svgTextUtils.lineCount(text); - var textNode = text.node(); + var textEl = g.select(legendItem ? + '.legendtext' : '.legendtitletext' + ); + var textLines = svgTextUtils.lineCount(textEl); + var textNode = textEl.node(); height = lineHeight * textLines; width = textNode ? Drawing.bBox(textNode).width : 0; // approximation to height offset to center the font // to avoid getBoundingClientRect - var textY = lineHeight * (0.3 + (1 - textLines) / 2); - svgTextUtils.positionText(text, constants.textGap, textY); + var textY = lineHeight * ((textLines - 1) / 2 - 0.3); + if(legendItem) { + svgTextUtils.positionText(textEl, constants.textGap, -textY); + } else { // case of title + svgTextUtils.positionText(textEl, constants.titlePad + bw, lineHeight + bw); + } + } + + if(legendItem) { + legendItem.lineHeight = lineHeight; + legendItem.height = Math.max(height, 16) + 3; + legendItem.width = width; + } else { // case of title + opts._titleWidth = width; + opts._titleHeight = height; + } +} + +function getTitleSize(opts) { + var w = 0; + var h = 0; + + var side = opts.title.side; + if(side) { + if(side.indexOf('left') !== -1) { + w = opts._titleWidth; + } + if(side.indexOf('top') !== -1) { + h = opts._titleHeight; + } } - legendItem.lineHeight = lineHeight; - legendItem.height = Math.max(height, 16) + 3; - legendItem.width = width; + return [w, h]; } /* @@ -514,6 +559,7 @@ function computeLegendDimensions(gd, groups, traces) { var fullLayout = gd._fullLayout; var opts = fullLayout.legend; var gs = fullLayout._size; + var isVertical = helpers.isVertical(opts); var isGrouped = helpers.isGrouped(opts); @@ -537,11 +583,15 @@ function computeLegendDimensions(gd, groups, traces) { var toggleRectWidth = 0; opts._width = 0; opts._height = 0; + var titleSize = getTitleSize(opts); if(isVertical) { traces.each(function(d) { var h = d[0].height; - Drawing.setTranslate(this, bw, itemGap + bw + opts._height + h / 2); + Drawing.setTranslate(this, + bw + titleSize[0], + bw + titleSize[1] + opts._height + h / 2 + itemGap + ); opts._height += h; opts._width = Math.max(opts._width, d[0].width); }); @@ -591,7 +641,10 @@ function computeLegendDimensions(gd, groups, traces) { var offsetY = 0; d3.select(this).selectAll('g.traces').each(function(d) { var h = d[0].height; - Drawing.setTranslate(this, 0, itemGap + bw + h / 2 + offsetY); + Drawing.setTranslate(this, + titleSize[0], + titleSize[1] + bw + itemGap + h / 2 + offsetY + ); offsetY += h; maxWidthInGroup = Math.max(maxWidthInGroup, textGap + d[0].width); }); @@ -634,7 +687,10 @@ function computeLegendDimensions(gd, groups, traces) { maxItemHeightInRow = 0; } - Drawing.setTranslate(this, bw + offsetX, itemGap + bw + h / 2 + offsetY); + Drawing.setTranslate(this, + titleSize[0] + bw + offsetX, + titleSize[1] + bw + offsetY + h / 2 + itemGap + ); rowWidth = offsetX + w + itemGap; offsetX += next; @@ -651,8 +707,19 @@ function computeLegendDimensions(gd, groups, traces) { } } - opts._width = Math.ceil(opts._width); - opts._height = Math.ceil(opts._height); + opts._width = Math.ceil( + Math.max( + opts._width + titleSize[0], + opts._titleWidth + 2 * (bw + constants.titlePad) + ) + ); + + opts._height = Math.ceil( + Math.max( + opts._height + titleSize[1], + opts._titleHeight + 2 * (bw + constants.itemGap) + ) + ); opts._effHeight = Math.min(opts._height, opts._maxHeight); diff --git a/src/components/legend/style.js b/src/components/legend/style.js index 6cacadb8ed7..7d6ce4124d7 100644 --- a/src/components/legend/style.js +++ b/src/components/legend/style.js @@ -84,6 +84,7 @@ module.exports = function style(s, gd) { .enter().append('g') .classed('legendpoints', true); }) + .each(styleSpatial) .each(styleWaterfalls) .each(styleFunnels) .each(styleBars) @@ -481,8 +482,132 @@ module.exports = function style(s, gd) { stylePie(pts, d0Mod, tMod); } } + + function styleSpatial(d) { // i.e. maninly traces having z and colorscale + var trace = d[0].trace; + + var useGradient; + var ptsData = []; + if(trace.visible) { + switch(trace.type) { + case 'histogram2d' : + case 'heatmap' : + ptsData = [ + ['M-15,-2V4H15V-2Z'] // similar to contour + ]; + useGradient = true; + break; + case 'choropleth' : + case 'choroplethmapbox' : + ptsData = [ + ['M-6,-6V6H6V-6Z'] + ]; + useGradient = true; + break; + case 'densitymapbox' : + ptsData = [ + ['M-6,0 a6,6 0 1,0 12,0 a 6,6 0 1,0 -12,0'] + ]; + useGradient = 'radial'; + break; + case 'cone' : + ptsData = [ + ['M-6,2 A2,2 0 0,0 -6,6 V6L6,4Z'], + ['M-6,-6 A2,2 0 0,0 -6,-2 L6,-4Z'], + ['M-6,-2 A2,2 0 0,0 -6,2 L6,0Z'] + ]; + useGradient = false; + break; + case 'streamtube' : + ptsData = [ + ['M-6,2 A2,2 0 0,0 -6,6 H6 A2,2 0 0,1 6,2 Z'], + ['M-6,-6 A2,2 0 0,0 -6,-2 H6 A2,2 0 0,1 6,-6 Z'], + ['M-6,-2 A2,2 0 0,0 -6,2 H6 A2,2 0 0,1 6,-2 Z'] + ]; + useGradient = false; + break; + case 'surface' : + ptsData = [ + ['M-6,-6 A2,3 0 0,0 -6,0 H6 A2,3 0 0,1 6,-6 Z'], + ['M-6,1 A2,3 0 0,1 -6,6 H6 A2,3 0 0,0 6,0 Z'] + ]; + useGradient = true; + break; + case 'mesh3d' : + ptsData = [ + ['M-6,6H0L-6,-6Z'], + ['M6,6H0L6,-6Z'], + ['M-6,-6H6L0,6Z'] + ]; + useGradient = false; + break; + case 'volume' : + ptsData = [ + ['M-6,6H0L-6,-6Z'], + ['M6,6H0L6,-6Z'], + ['M-6,-6H6L0,6Z'] + ]; + useGradient = true; + break; + case 'isosurface': + ptsData = [ + ['M-6,6H0L-6,-6Z'], + ['M6,6H0L6,-6Z'], + ['M-6,-6 A12,24 0 0,0 6,-6 L0,6Z'] + ]; + useGradient = false; + break; + } + } + + var pts = d3.select(this).select('g.legendpoints') + .selectAll('path.legend3dandfriends') + .data(ptsData); + pts.enter().append('path').classed('legend3dandfriends', true) + .attr('transform', 'translate(20,0)') + .style('stroke-miterlimit', 1); + pts.exit().remove(); + + pts.each(function(dd, i) { + var pt = d3.select(this); + + var cOpts = extractOpts(trace); + var colorscale = cOpts.colorscale; + var reversescale = cOpts.reversescale; + var fillGradient = function(s) { + if(s.size()) { + var gradientID = 'legendfill-' + trace.uid; + Drawing.gradient(s, gd, gradientID, + getGradientDirection(reversescale, useGradient === 'radial'), + colorscale, 'fill'); + } + }; + + var fillColor; + if(!colorscale) { + var color = trace.vertexcolor || trace.facecolor || trace.color; + fillColor = Lib.isArrayOrTypedArray(color) ? (color[i] || color[0]) : color; + } else { + if(!useGradient) { + var len = colorscale.length; + fillColor = + i === 0 ? colorscale[reversescale ? len - 1 : 0][1] : // minimum + i === 1 ? colorscale[reversescale ? 0 : len - 1][1] : // maximum + colorscale[Math.floor((len - 1) / 2)][1]; // middle + } + } + + pt.attr('d', dd[0]); + if(fillColor) { + pt.call(Color.fill, fillColor); + } else { + pt.call(fillGradient); + } + }); + } }; -function getGradientDirection(reversescale) { - return reversescale ? 'horizontal' : 'horizontalreversed'; +function getGradientDirection(reversescale, isRadial) { + var str = isRadial ? 'radial' : 'horizontal'; + return str + (reversescale ? '' : 'reversed'); } diff --git a/src/plots/plots.js b/src/plots/plots.js index ea831406ad2..48f37433797 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1291,9 +1291,14 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac coerce('meta'); if(Registry.traceIs(traceOut, 'showLegend')) { - traceOut._dfltShowLegend = true; - coerce('showlegend'); + Lib.coerce(traceIn, traceOut, + _module.attributes.showlegend ? _module.attributes : plots.attributes, + 'showlegend' + ); + coerce('legendgroup'); + + traceOut._dfltShowLegend = true; } else { traceOut._dfltShowLegend = false; } diff --git a/src/traces/choropleth/attributes.js b/src/traces/choropleth/attributes.js index 8186cb54b17..413d2438fc5 100644 --- a/src/traces/choropleth/attributes.js +++ b/src/traces/choropleth/attributes.js @@ -77,7 +77,8 @@ module.exports = extendFlat({ editType: 'calc', flags: ['location', 'z', 'text', 'name'] }), - hovertemplate: hovertemplateAttrs() + hovertemplate: hovertemplateAttrs(), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }, colorScaleAttrs('', { diff --git a/src/traces/choropleth/index.js b/src/traces/choropleth/index.js index 1445b7a07eb..06a265df6c0 100644 --- a/src/traces/choropleth/index.js +++ b/src/traces/choropleth/index.js @@ -23,7 +23,7 @@ module.exports = { moduleType: 'trace', name: 'choropleth', basePlotModule: require('../../plots/geo'), - categories: ['geo', 'noOpacity'], + categories: ['geo', 'noOpacity', 'showLegend'], meta: { description: [ 'The data that describes the choropleth value-to-color mapping', diff --git a/src/traces/choroplethmapbox/attributes.js b/src/traces/choroplethmapbox/attributes.js index 31c932e1273..08e54b7d830 100644 --- a/src/traces/choroplethmapbox/attributes.js +++ b/src/traces/choroplethmapbox/attributes.js @@ -11,7 +11,7 @@ var choroplethAttrs = require('../choropleth/attributes'); var colorScaleAttrs = require('../../components/colorscale/attributes'); var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs; - +var baseAttrs = require('../../plots/attributes'); var extendFlat = require('../../lib/extend').extendFlat; module.exports = extendFlat({ @@ -103,7 +103,8 @@ module.exports = extendFlat({ }, hoverinfo: choroplethAttrs.hoverinfo, - hovertemplate: hovertemplateAttrs({}, {keys: ['properties']}) + hovertemplate: hovertemplateAttrs({}, {keys: ['properties']}), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }, colorScaleAttrs('', { diff --git a/src/traces/choroplethmapbox/index.js b/src/traces/choroplethmapbox/index.js index 4e9db4c81dd..1bda0407438 100644 --- a/src/traces/choroplethmapbox/index.js +++ b/src/traces/choroplethmapbox/index.js @@ -52,7 +52,7 @@ module.exports = { moduleType: 'trace', name: 'choroplethmapbox', basePlotModule: require('../../plots/mapbox'), - categories: ['mapbox', 'gl', 'noOpacity'], + categories: ['mapbox', 'gl', 'noOpacity', 'showLegend'], meta: { hr_name: 'choropleth_mapbox', description: [ diff --git a/src/traces/cone/attributes.js b/src/traces/cone/attributes.js index 5e263c86515..e76d27de7cd 100644 --- a/src/traces/cone/attributes.js +++ b/src/traces/cone/attributes.js @@ -166,7 +166,9 @@ var attrs = { editType: 'calc', description: 'Same as `text`.' }, - hovertemplate: hovertemplateAttrs({editType: 'calc'}, {keys: ['norm']}) + + hovertemplate: hovertemplateAttrs({editType: 'calc'}, {keys: ['norm']}), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }; extendFlat(attrs, colorScaleAttrs('', { diff --git a/src/traces/cone/index.js b/src/traces/cone/index.js index e786cb9d47e..eadd43638d2 100644 --- a/src/traces/cone/index.js +++ b/src/traces/cone/index.js @@ -12,7 +12,7 @@ module.exports = { moduleType: 'trace', name: 'cone', basePlotModule: require('../../plots/gl3d'), - categories: ['gl3d'], + categories: ['gl3d', 'showLegend'], attributes: require('./attributes'), supplyDefaults: require('./defaults'), diff --git a/src/traces/densitymapbox/attributes.js b/src/traces/densitymapbox/attributes.js index 3d1ffc03975..1dd3cccde1d 100644 --- a/src/traces/densitymapbox/attributes.js +++ b/src/traces/densitymapbox/attributes.js @@ -84,7 +84,8 @@ module.exports = extendFlat({ hoverinfo: extendFlat({}, baseAttrs.hoverinfo, { flags: ['lon', 'lat', 'z', 'text', 'name'] }), - hovertemplate: hovertemplateAttrs() + hovertemplate: hovertemplateAttrs(), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }, colorScaleAttrs('', { cLetter: 'z', diff --git a/src/traces/densitymapbox/index.js b/src/traces/densitymapbox/index.js index 8c029a22afa..1f4e5d36805 100644 --- a/src/traces/densitymapbox/index.js +++ b/src/traces/densitymapbox/index.js @@ -37,7 +37,7 @@ module.exports = { moduleType: 'trace', name: 'densitymapbox', basePlotModule: require('../../plots/mapbox'), - categories: ['mapbox', 'gl'], + categories: ['mapbox', 'gl', 'showLegend'], meta: { hr_name: 'density_mapbox', description: [ diff --git a/src/traces/heatmap/attributes.js b/src/traces/heatmap/attributes.js index f63fb40a1c9..991fe18fac2 100644 --- a/src/traces/heatmap/attributes.js +++ b/src/traces/heatmap/attributes.js @@ -9,6 +9,7 @@ 'use strict'; var scatterAttrs = require('../scatter/attributes'); +var baseAttrs = require('../../plots/attributes'); var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs; var colorScaleAttrs = require('../../components/colorscale/attributes'); var FORMAT_LINK = require('../../constants/docs').FORMAT_LINK; @@ -130,7 +131,8 @@ module.exports = extendFlat({ FORMAT_LINK ].join(' ') }, - hovertemplate: hovertemplateAttrs() + hovertemplate: hovertemplateAttrs(), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }, { transforms: undefined }, diff --git a/src/traces/heatmap/index.js b/src/traces/heatmap/index.js index cea242fc0ac..259a145fbd0 100644 --- a/src/traces/heatmap/index.js +++ b/src/traces/heatmap/index.js @@ -20,7 +20,7 @@ module.exports = { moduleType: 'trace', name: 'heatmap', basePlotModule: require('../../plots/cartesian'), - categories: ['cartesian', 'svg', '2dMap'], + categories: ['cartesian', 'svg', '2dMap', 'showLegend'], meta: { description: [ 'The data that describes the heatmap value-to-color mapping', diff --git a/src/traces/histogram2d/attributes.js b/src/traces/histogram2d/attributes.js index 2fa6ffbd04f..657c2bc22e1 100644 --- a/src/traces/histogram2d/attributes.js +++ b/src/traces/histogram2d/attributes.js @@ -11,6 +11,7 @@ var histogramAttrs = require('../histogram/attributes'); var makeBinAttrs = require('../histogram/bin_attributes'); var heatmapAttrs = require('../heatmap/attributes'); +var baseAttrs = require('../../plots/attributes'); var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs; var colorScaleAttrs = require('../../components/colorscale/attributes'); @@ -72,7 +73,8 @@ module.exports = extendFlat( ygap: heatmapAttrs.ygap, zsmooth: heatmapAttrs.zsmooth, zhoverformat: heatmapAttrs.zhoverformat, - hovertemplate: hovertemplateAttrs({}, {keys: 'z'}) + hovertemplate: hovertemplateAttrs({}, {keys: 'z'}), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }, colorScaleAttrs('', {cLetter: 'z', autoColorDflt: false}) ); diff --git a/src/traces/histogram2d/index.js b/src/traces/histogram2d/index.js index 16e37189051..b17fabea62b 100644 --- a/src/traces/histogram2d/index.js +++ b/src/traces/histogram2d/index.js @@ -9,7 +9,6 @@ 'use strict'; module.exports = { - attributes: require('./attributes'), supplyDefaults: require('./defaults'), crossTraceDefaults: require('../histogram/cross_trace_defaults'), @@ -24,7 +23,7 @@ module.exports = { moduleType: 'trace', name: 'histogram2d', basePlotModule: require('../../plots/cartesian'), - categories: ['cartesian', 'svg', '2dMap', 'histogram'], + categories: ['cartesian', 'svg', '2dMap', 'histogram', 'showLegend'], meta: { hrName: 'histogram_2d', description: [ diff --git a/src/traces/isosurface/attributes.js b/src/traces/isosurface/attributes.js index c294ca45f01..4b7369c3d7f 100644 --- a/src/traces/isosurface/attributes.js +++ b/src/traces/isosurface/attributes.js @@ -232,7 +232,8 @@ var attrs = module.exports = overrideAll(extendFlat({ arrayOk: true, description: 'Same as `text`.' }, - hovertemplate: hovertemplateAttrs() + hovertemplate: hovertemplateAttrs(), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }, colorScaleAttrs('', { diff --git a/src/traces/isosurface/index.js b/src/traces/isosurface/index.js index 193167ba186..5c6ec465a09 100644 --- a/src/traces/isosurface/index.js +++ b/src/traces/isosurface/index.js @@ -21,7 +21,7 @@ module.exports = { moduleType: 'trace', name: 'isosurface', basePlotModule: require('../../plots/gl3d'), - categories: ['gl3d'], + categories: ['gl3d', 'showLegend'], meta: { description: [ 'Draws isosurfaces between iso-min and iso-max values with coordinates given by', diff --git a/src/traces/mesh3d/attributes.js b/src/traces/mesh3d/attributes.js index db7c1963568..9641ae6ef1a 100644 --- a/src/traces/mesh3d/attributes.js +++ b/src/traces/mesh3d/attributes.js @@ -238,5 +238,6 @@ colorScaleAttrs('', { editType: 'calc' }, surfaceAttrs.lighting), - hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {editType: 'calc'}) + hoverinfo: extendFlat({}, baseAttrs.hoverinfo, {editType: 'calc'}), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }); diff --git a/src/traces/mesh3d/index.js b/src/traces/mesh3d/index.js index a1277d17e56..2af7fa64efd 100644 --- a/src/traces/mesh3d/index.js +++ b/src/traces/mesh3d/index.js @@ -21,7 +21,7 @@ module.exports = { moduleType: 'trace', name: 'mesh3d', basePlotModule: require('../../plots/gl3d'), - categories: ['gl3d'], + categories: ['gl3d', 'showLegend'], meta: { description: [ 'Draws sets of triangles with coordinates given by', diff --git a/src/traces/streamtube/attributes.js b/src/traces/streamtube/attributes.js index 9cf58c2b3fb..7b8e949a2c3 100644 --- a/src/traces/streamtube/attributes.js +++ b/src/traces/streamtube/attributes.js @@ -144,7 +144,8 @@ var attrs = { 'tubeu', 'tubev', 'tubew', 'norm', 'divergence' ] - }) + }), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }; extendFlat(attrs, colorScaleAttrs('', { diff --git a/src/traces/streamtube/index.js b/src/traces/streamtube/index.js index bdb5270e00d..eed0b46e569 100644 --- a/src/traces/streamtube/index.js +++ b/src/traces/streamtube/index.js @@ -12,7 +12,7 @@ module.exports = { moduleType: 'trace', name: 'streamtube', basePlotModule: require('../../plots/gl3d'), - categories: ['gl3d'], + categories: ['gl3d', 'showLegend'], attributes: require('./attributes'), supplyDefaults: require('./defaults'), diff --git a/src/traces/surface/attributes.js b/src/traces/surface/attributes.js index 6d9ae82c2f1..108ab8608ea 100644 --- a/src/traces/surface/attributes.js +++ b/src/traces/surface/attributes.js @@ -310,7 +310,8 @@ colorScaleAttrs('', { }) }, - hoverinfo: extendFlat({}, baseAttrs.hoverinfo) + hoverinfo: extendFlat({}, baseAttrs.hoverinfo), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}), }), 'calc', 'nested'); attrs.x.editType = attrs.y.editType = attrs.z.editType = 'calc+clearAxisTypes'; diff --git a/src/traces/surface/index.js b/src/traces/surface/index.js index 83f284b878f..f30cd6605f4 100644 --- a/src/traces/surface/index.js +++ b/src/traces/surface/index.js @@ -21,7 +21,7 @@ module.exports = { moduleType: 'trace', name: 'surface', basePlotModule: require('../../plots/gl3d'), - categories: ['gl3d', '2dMap'], + categories: ['gl3d', '2dMap', 'showLegend'], meta: { description: [ 'The data the describes the coordinates of the surface is set in `z`.', diff --git a/src/traces/volume/attributes.js b/src/traces/volume/attributes.js index adf1e14eb6f..114ceb6ce17 100644 --- a/src/traces/volume/attributes.js +++ b/src/traces/volume/attributes.js @@ -86,7 +86,8 @@ colorScaleAttrs('', { flatshading: isosurfaceAttrs.flatshading, contour: isosurfaceAttrs.contour, - hoverinfo: extendFlat({}, baseAttrs.hoverinfo) + hoverinfo: extendFlat({}, baseAttrs.hoverinfo), + showlegend: extendFlat({}, baseAttrs.showlegend, {dflt: false}) }), 'calc', 'nested'); attrs.x.editType = attrs.y.editType = attrs.z.editType = attrs.value.editType = 'calc+clearAxisTypes'; diff --git a/src/traces/volume/index.js b/src/traces/volume/index.js index 37c187a1555..d5e60d66509 100644 --- a/src/traces/volume/index.js +++ b/src/traces/volume/index.js @@ -21,7 +21,7 @@ module.exports = { moduleType: 'trace', name: 'volume', basePlotModule: require('../../plots/gl3d'), - categories: ['gl3d'], + categories: ['gl3d', 'showLegend'], meta: { description: [ 'Draws volume trace between iso-min and iso-max values with coordinates given by', diff --git a/test/image/baselines/geo_choropleth-legend.png b/test/image/baselines/geo_choropleth-legend.png new file mode 100644 index 00000000000..421fb0fcb3c Binary files /dev/null and b/test/image/baselines/geo_choropleth-legend.png differ diff --git a/test/image/baselines/geo_choropleth-usa_legend.png b/test/image/baselines/geo_choropleth-usa_legend.png new file mode 100644 index 00000000000..86ff75d9a73 Binary files /dev/null and b/test/image/baselines/geo_choropleth-usa_legend.png differ diff --git a/test/image/baselines/gl3d_traces-with-legend.png b/test/image/baselines/gl3d_traces-with-legend.png new file mode 100644 index 00000000000..3df06dc64bb Binary files /dev/null and b/test/image/baselines/gl3d_traces-with-legend.png differ diff --git a/test/image/baselines/heatmap_legend.png b/test/image/baselines/heatmap_legend.png new file mode 100644 index 00000000000..19045a5f1ac Binary files /dev/null and b/test/image/baselines/heatmap_legend.png differ diff --git a/test/image/baselines/histogram2d_legend.png b/test/image/baselines/histogram2d_legend.png new file mode 100644 index 00000000000..96e54f2a9ba Binary files /dev/null and b/test/image/baselines/histogram2d_legend.png differ diff --git a/test/image/baselines/legend_mathjax_title_and_items.png b/test/image/baselines/legend_mathjax_title_and_items.png new file mode 100644 index 00000000000..348ad2b058e Binary files /dev/null and b/test/image/baselines/legend_mathjax_title_and_items.png differ diff --git a/test/image/baselines/legend_scroll_with_title.png b/test/image/baselines/legend_scroll_with_title.png new file mode 100644 index 00000000000..78816166eeb Binary files /dev/null and b/test/image/baselines/legend_scroll_with_title.png differ diff --git a/test/image/baselines/mapbox_choropleth0-legend.png b/test/image/baselines/mapbox_choropleth0-legend.png new file mode 100644 index 00000000000..a4ee88487b7 Binary files /dev/null and b/test/image/baselines/mapbox_choropleth0-legend.png differ diff --git a/test/image/baselines/mapbox_density-mulitple_legend.png b/test/image/baselines/mapbox_density-mulitple_legend.png new file mode 100644 index 00000000000..d6a0f0d0fe3 Binary files /dev/null and b/test/image/baselines/mapbox_density-mulitple_legend.png differ diff --git a/test/image/baselines/mapbox_density0-legend.png b/test/image/baselines/mapbox_density0-legend.png new file mode 100644 index 00000000000..191dc382b00 Binary files /dev/null and b/test/image/baselines/mapbox_density0-legend.png differ diff --git a/test/image/mocks/geo_choropleth-legend.json b/test/image/mocks/geo_choropleth-legend.json new file mode 100644 index 00000000000..e748e2a16a7 --- /dev/null +++ b/test/image/mocks/geo_choropleth-legend.json @@ -0,0 +1,67 @@ +{ + "data": [ + { + "type": "choropleth", + "name": "Canada", + "locations": ["CAN"], + "z": [1], + "colorscale": [[0, "blue"], [1, "blue"]], + "showscale": false, + "showlegend": true + }, + { + "type": "choropleth", + "name": "USA", + "locations": ["USA"], + "z": [1], + "colorscale": [[0, "orange"], [1, "orange"]], + "showscale": false, + "showlegend": true + }, + { + "type": "choropleth", + "name": "Brazil", + "locations": ["BRA"], + "z": [1], + "colorscale": [[0, "green"], [1, "green"]], + "showscale": false, + "showlegend": true + }, + { + "type": "choropleth", + "name": "China", + "locations": ["CHN"], + "z": [1], + "colorscale": [[0, "red"], [1, "red"]], + "showscale": false, + "showlegend": true + } + ], + "layout": { + "title": { + "text": "choropleth with legends and legend title" + }, + "legend": { + "borderwidth": 5, + "bordercolor": "gray", + "bgcolor": "yellow", + "orientation": "h", + "title": { + "text": "World Countries
legends on different lines!", + "font": { + "size": 15, + "family": "Times New Roman" + } + } + }, + "paper_bgcolor": "lightblue", + "width": 500, + "height": 350, + "margin": { + "t": 50, + "b": 0, + "l": 0, + "r": 0 + } + } +} diff --git a/test/image/mocks/geo_choropleth-usa_legend.json b/test/image/mocks/geo_choropleth-usa_legend.json new file mode 100644 index 00000000000..be858ca2980 --- /dev/null +++ b/test/image/mocks/geo_choropleth-usa_legend.json @@ -0,0 +1,201 @@ +{ + "data": [ + { + "autocolorscale": false, + "showlegend": true, + "showscale": false, + "colorscale": "Portland", + "text": [ + "Alabama
--
Beef: 34.4
Pork: 10.6
Poultry: 481.0
Dairy: 4.06
--
Fruits: 25.11
Veggies: 14.33
--
Wheat: 70.0
Corn: 34.9
Cotton: 317.61", + "Alaska
--
Beef: 0.2
Pork: 0.1
Poultry: 0.0
Dairy: 0.19
--
Fruits: 0.0
Veggies: 1.56
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Arizona
--
Beef: 71.3
Pork: 17.9
Poultry: 0.0
Dairy: 105.48
--
Fruits: 60.27
Veggies: 386.91
--
Wheat: 48.7
Corn: 7.3
Cotton: 423.95", + "Arkansas
--
Beef: 53.2
Pork: 29.4
Poultry: 562.9
Dairy: 3.53
--
Fruits: 6.88
Veggies: 11.45
--
Wheat: 114.5
Corn: 69.5
Cotton: 665.44", + " California
--
Beef: 228.7
Pork: 11.1
Poultry: 225.4
Dairy: 929.95
--
Fruits: 8736.4
Veggies: 2106.79
--
Wheat: 249.3
Corn: 34.6
Cotton: 1064.95", + "Colorado
--
Beef: 261.4
Pork: 66.0
Poultry: 14.0
Dairy: 71.94
--
Fruits: 17.99
Veggies: 118.27
--
Wheat: 400.5
Corn: 183.2
Cotton: 0.0", + "Connecticut
--
Beef: 1.1
Pork: 0.1
Poultry: 6.9
Dairy: 9.49
--
Fruits: 13.1
Veggies: 11.16
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Delaware
--
Beef: 0.4
Pork: 0.6
Poultry: 114.7
Dairy: 2.3
--
Fruits: 1.53
Veggies: 20.03
--
Wheat: 22.9
Corn: 26.9
Cotton: 0.0", + "Florida
--
Beef: 42.6
Pork: 0.9
Poultry: 56.9
Dairy: 66.31
--
Fruits: 1371.36
Veggies: 450.86
--
Wheat: 1.8
Corn: 3.5
Cotton: 78.24", + "Georgia
--
Beef: 31.0
Pork: 18.9
Poultry: 630.4
Dairy: 38.38
--
Fruits: 233.51
Veggies: 154.77
--
Wheat: 65.4
Corn: 57.8
Cotton: 1154.07", + "Hawaii
--
Beef: 4.0
Pork: 0.7
Poultry: 1.3
Dairy: 1.16
--
Fruits: 55.51
Veggies: 24.83
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Idaho
--
Beef: 119.8
Pork: 0.0
Poultry: 2.4
Dairy: 294.6
--
Fruits: 21.64
Veggies: 319.19
--
Wheat: 568.2
Corn: 24.0
Cotton: 0.0", + "Illinois
--
Beef: 53.7
Pork: 394.0
Poultry: 14.0
Dairy: 45.82
--
Fruits: 12.53
Veggies: 39.95
--
Wheat: 223.8
Corn: 2228.5
Cotton: 0.0", + "Indiana
--
Beef: 21.9
Pork: 341.9
Poultry: 165.6
Dairy: 89.7
--
Fruits: 12.98
Veggies: 37.89
--
Wheat: 114.0
Corn: 1123.2
Cotton: 0.0", + "Iowa
--
Beef: 289.8
Pork: 1895.6
Poultry: 155.6
Dairy: 107.0
--
Fruits: 3.24
Veggies: 7.1
--
Wheat: 3.1
Corn: 2529.8
Cotton: 0.0", + "Kansas
--
Beef: 659.3
Pork: 179.4
Poultry: 6.4
Dairy: 65.45
--
Fruits: 3.11
Veggies: 9.32
--
Wheat: 1426.5
Corn: 457.3
Cotton: 43.98", + "Kentucky
--
Beef: 54.8
Pork: 34.2
Poultry: 151.3
Dairy: 28.27
--
Fruits: 6.6
Veggies: 0.0
--
Wheat: 149.3
Corn: 179.1
Cotton: 0.0", + "Louisiana
--
Beef: 19.8
Pork: 0.8
Poultry: 77.2
Dairy: 6.02
--
Fruits: 17.83
Veggies: 17.25
--
Wheat: 78.7
Corn: 91.4
Cotton: 280.42", + "Maine
--
Beef: 1.4
Pork: 0.5
Poultry: 10.4
Dairy: 16.18
--
Fruits: 52.01
Veggies: 62.9
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Maryland
--
Beef: 5.6
Pork: 3.1
Poultry: 127.0
Dairy: 24.81
--
Fruits: 12.9
Veggies: 20.43
--
Wheat: 55.8
Corn: 54.1
Cotton: 0.0", + "Massachusetts
--
Beef: 0.6
Pork: 0.5
Poultry: 0.6
Dairy: 5.81
--
Fruits: 80.83
Veggies: 21.13
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Michigan
--
Beef: 37.7
Pork: 118.1
Poultry: 32.6
Dairy: 214.82
--
Fruits: 257.69
Veggies: 189.96
--
Wheat: 247.0
Corn: 381.5
Cotton: 0.0", + "Minnesota
--
Beef: 112.3
Pork: 740.4
Poultry: 189.2
Dairy: 218.05
--
Fruits: 7.91
Veggies: 120.37
--
Wheat: 538.1
Corn: 1264.3
Cotton: 0.0", + "Mississippi
--
Beef: 12.8
Pork: 30.4
Poultry: 370.8
Dairy: 5.45
--
Fruits: 17.04
Veggies: 27.87
--
Wheat: 102.2
Corn: 110.0
Cotton: 494.75", + "Missouri
--
Beef: 137.2
Pork: 277.3
Poultry: 196.1
Dairy: 34.26
--
Fruits: 13.18
Veggies: 17.9
--
Wheat: 161.7
Corn: 428.8
Cotton: 345.29", + "Montana
--
Beef: 105.0
Pork: 16.7
Poultry: 1.7
Dairy: 6.82
--
Fruits: 3.3
Veggies: 45.27
--
Wheat: 1198.1
Corn: 5.4
Cotton: 0.0", + "Nebraska
--
Beef: 762.2
Pork: 262.5
Poultry: 31.4
Dairy: 30.07
--
Fruits: 2.16
Veggies: 53.5
--
Wheat: 292.3
Corn: 1735.9
Cotton: 0.0", + "Nevada
--
Beef: 21.8
Pork: 0.2
Poultry: 0.0
Dairy: 16.57
--
Fruits: 1.19
Veggies: 27.93
--
Wheat: 5.4
Corn: 0.0
Cotton: 0.0", + "New Hampshire
--
Beef: 0.6
Pork: 0.2
Poultry: 0.8
Dairy: 7.46
--
Fruits: 7.98
Veggies: 4.5
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "New Jersey
--
Beef: 0.8
Pork: 0.4
Poultry: 4.6
Dairy: 3.37
--
Fruits: 109.45
Veggies: 56.54
--
Wheat: 6.7
Corn: 10.1
Cotton: 0.0", + "New Mexico
--
Beef: 117.2
Pork: 0.1
Poultry: 0.3
Dairy: 191.01
--
Fruits: 101.9
Veggies: 43.88
--
Wheat: 13.9
Corn: 11.2
Cotton: 72.62", + "New York
--
Beef: 22.2
Pork: 5.8
Poultry: 17.7
Dairy: 331.8
--
Fruits: 202.56
Veggies: 143.37
--
Wheat: 29.9
Corn: 106.1
Cotton: 0.0", + "North Carolina
--
Beef: 24.8
Pork: 702.8
Poultry: 598.4
Dairy: 24.9
--
Fruits: 74.47
Veggies: 150.45
--
Wheat: 200.3
Corn: 92.2
Cotton: 470.86", + "North Dakota
--
Beef: 78.5
Pork: 16.1
Poultry: 0.5
Dairy: 8.14
--
Fruits: 0.25
Veggies: 130.79
--
Wheat: 1664.5
Corn: 236.1
Cotton: 0.0", + "Ohio
--
Beef: 36.2
Pork: 199.1
Poultry: 129.9
Dairy: 134.57
--
Fruits: 27.21
Veggies: 53.53
--
Wheat: 207.4
Corn: 535.1
Cotton: 0.0", + "Oklahoma
--
Beef: 337.6
Pork: 265.3
Poultry: 131.1
Dairy: 24.35
--
Fruits: 9.24
Veggies: 8.9
--
Wheat: 324.8
Corn: 27.5
Cotton: 110.54", + "Oregon
--
Beef: 58.8
Pork: 1.4
Poultry: 14.2
Dairy: 63.66
--
Fruits: 315.04
Veggies: 126.5
--
Wheat: 320.3
Corn: 11.7
Cotton: 0.0", + "Pennsylvania
--
Beef: 50.9
Pork: 91.3
Poultry: 169.8
Dairy: 280.87
--
Fruits: 89.48
Veggies: 38.26
--
Wheat: 41.0
Corn: 112.1
Cotton: 0.0", + "Rhode Island
--
Beef: 0.1
Pork: 0.1
Poultry: 0.2
Dairy: 0.52
--
Fruits: 2.83
Veggies: 3.02
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "South Carolina
--
Beef: 15.2
Pork: 10.9
Poultry: 186.5
Dairy: 7.62
--
Fruits: 53.45
Veggies: 42.66
--
Wheat: 55.3
Corn: 32.1
Cotton: 206.1", + "South Dakota
--
Beef: 193.5
Pork: 160.2
Poultry: 29.3
Dairy: 46.77
--
Fruits: 0.8
Veggies: 4.06
--
Wheat: 704.5
Corn: 643.6
Cotton: 0.0", + "Tennessee
--
Beef: 51.1
Pork: 17.6
Poultry: 82.4
Dairy: 21.18
--
Fruits: 6.23
Veggies: 24.67
--
Wheat: 100.0
Corn: 88.8
Cotton: 363.83", + "Texas
--
Beef: 961.0
Pork: 42.7
Poultry: 339.2
Dairy: 240.55
--
Fruits: 99.9
Veggies: 115.23
--
Wheat: 309.7
Corn: 167.2
Cotton: 2308.76", + "Utah
--
Beef: 27.9
Pork: 59.0
Poultry: 23.1
Dairy: 48.6
--
Fruits: 12.34
Veggies: 6.6
--
Wheat: 42.8
Corn: 5.3
Cotton: 0.0", + "Vermont
--
Beef: 6.2
Pork: 0.2
Poultry: 0.9
Dairy: 65.98
--
Fruits: 8.01
Veggies: 4.05
--
Wheat: 0.0
Corn: 0.0
Cotton: 0.0", + "Virginia
--
Beef: 39.5
Pork: 16.9
Poultry: 164.7
Dairy: 47.85
--
Fruits: 36.48
Veggies: 27.25
--
Wheat: 77.5
Corn: 39.5
Cotton: 64.84", + "Washington
--
Beef: 59.2
Pork: 0.0
Poultry: 35.6
Dairy: 154.18
--
Fruits: 1738.57
Veggies: 363.79
--
Wheat: 786.3
Corn: 29.5
Cotton: 0.0", + "West Virginia
--
Beef: 12.0
Pork: 0.3
Poultry: 45.4
Dairy: 3.9
--
Fruits: 11.54
Veggies: 0.0
--
Wheat: 1.6
Corn: 3.5
Cotton: 0.0", + "Wisconsin
--
Beef: 107.3
Pork: 38.6
Poultry: 34.5
Dairy: 633.6
--
Fruits: 133.8
Veggies: 148.99
--
Wheat: 96.7
Corn: 460.5
Cotton: 0.0", + "Wyoming
--
Beef: 75.1
Pork: 33.2
Poultry: 0.1
Dairy: 2.89
--
Fruits: 0.17
Veggies: 10.23
--
Wheat: 20.7
Corn: 9.0
Cotton: 0.0" + ], + "locations": [ + "AL", + "AK", + "AZ", + "AR", + "CA", + "CO", + "CT", + "DE", + "FL", + "GA", + "HI", + "ID", + "IL", + "IN", + "IA", + "KS", + "KY", + "LA", + "ME", + "MD", + "MA", + "MI", + "MN", + "MS", + "MO", + "MT", + "NE", + "NV", + "NH", + "NJ", + "NM", + "NY", + "NC", + "ND", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VT", + "VA", + "WA", + "WV", + "WI", + "WY" + ], + "colorbar": { + "title": { + "text": "Millions USD" + } + }, + "type": "choropleth", + "z": [ + 1390.63, + 13.31, + 1463.17, + 3586.02, + 16472.88, + 1851.33, + 259.62, + 282.19, + 3764.09, + 2860.84, + 401.84, + 2078.89, + 8709.48, + 5050.23, + 11273.76, + 4589.01, + 1889.15, + 1914.23, + 278.37, + 692.75, + 248.65, + 3164.16, + 7192.33, + 2170.8, + 3933.42, + 1718, + 7114.13, + 139.89, + 73.06, + 500.4, + 751.58, + 1488.9, + 3806.05, + 3761.96, + 3979.79, + 1646.41, + 1794.57, + 1969.87, + 31.59, + 929.93, + 3770.19, + 1535.13, + 6648.22, + 453.39, + 180.14, + 1146.48, + 3894.81, + 138.89, + 3090.23, + 349.69 + ], + "locationmode": "USA-states", + "name": "Exports" + } + ], + "layout": { + "autosize": true, + "title": { + "text": "2011 US Agriculture Exports by State" + }, + "legend": { + "title": { + "text": "2011 US Agriculture
Exports by State" + } + }, + "height": 250, + "width": 450, + "margin": { + "t": 50, + "b": 0, + "l": 0, + "r": 0 + }, + "geo": { + "showlakes": true, + "scope": "usa", + "projection": { + "type": "albers usa" + }, + "lakecolor": "rgb(255, 255, 255)" + } + } +} diff --git a/test/image/mocks/gl3d_traces-with-legend.json b/test/image/mocks/gl3d_traces-with-legend.json new file mode 100644 index 00000000000..6a553ebf48f --- /dev/null +++ b/test/image/mocks/gl3d_traces-with-legend.json @@ -0,0 +1,245 @@ +{ + "data": [{ + "showlegend": true, + "x": [0, 1, 0, 1, 0, 1, 0, 1], + "y": [0, 0, 1, 1, 0, 0, 1, 1], + "z": [0, 0, 0, 0, 1, 1, 1, 1], + "i": [0, 3, 4, 7, 0, 6, 1, 7, 0, 5, 2, 7], + "j": [1, 2, 5, 6, 2, 4, 3, 5, 4, 1, 6, 3], + "k": [3, 0, 7, 4, 6, 0, 7, 1, 5, 0, 7, 2], + "color": "#FF0", + "flatshading": true, + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "type": "mesh3d", + "name": "mesh3d", + "scene": "scene1" + }, { + "showlegend": true, + "x":[0, -1, -2, 0], + "y":[0, 0, 1, 2], + "z":[0, 2, 0, 1], + "i":[0, 0, 0, 1], + "j":[1, 2, 3, 2], + "k":[2, 3, 1, 3], + "facecolor": ["#F00", "#FF0", "#0F0", "#0FF"], + "flatshading": true, + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "type": "mesh3d", + "name": "mesh3d", + "scene": "scene2" + }, { + "showlegend": true, + "x": [0, 1, 0, 1, 0, 1, 0, 1], + "y": [0, 0, 1, 1, 0, 0, 1, 1], + "z": [0, 0, 0, 0, 1, 1, 1, 1], + "i": [0, 3, 4, 7, 0, 6, 1, 7, 0, 5, 2, 7], + "j": [1, 2, 5, 6, 2, 4, 3, 5, 4, 1, 6, 3], + "k": [3, 0, 7, 4, 6, 0, 7, 1, 5, 0, 7, 2], + "intensity": [-1, 2, -3, 4, -5, 6, -7, 8], + "showscale": false, + "flatshading": true, + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "type": "mesh3d", + "name": "mesh3d", + "scene": "scene3" + }, { + "showlegend": true, + "x":[0, -1, -2, 0], + "y":[0, 0, 1, 2], + "z":[0, 2, 0, 1], + "i":[0, 0, 0, 1], + "j":[1, 2, 3, 2], + "k":[2, 3, 1, 3], + "vertexcolor": ["#F00", "#FF0", "#0F0", "#0FF"], + "flatshading": true, + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "type": "mesh3d", + "name": "mesh3d", + "scene": "scene4" + }, { + "showlegend": true, + "x": [-1, 0, 1], + "y": [-1, 0, 1], + "z": [ + [0, 1, 0], + [1, 0, 1], + [0, 1, 0] + ], + "surfacecolor": [ + [0, 1, 2], + [0, 1, 2], + [0, 1, 2] + ], + "showscale": false, + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "type": "surface", + "name": "surface", + "scene": "scene5" + }, { + "showlegend": true, + "x": [-1, 0, 1], + "y": [-1, 0, 1], + "z": [ + [0, 1, 0], + [1, 0, 1], + [0, 1, 0] + ], + "showscale": false, + "reversescale": true, + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "type": "surface", + "name": "surface", + "scene": "scene6" + }, { + "showlegend": true, + "x": [0, 0, 1, 1, 0, 0, 1, 1], + "y": [0, 1, 1, 0, 0, 1, 1, 0], + "z": [0, 0, 0, 0, 1, 1, 1, 1], + "u": [0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1], + "v": [0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5], + "w": [0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1], + "showscale": false, + "reversescale": true, + "colorscale": "Viridis", + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "type": "cone", + "name": "cone", + "scene": "scene7" + }, { + "showlegend": true, + "x": [0, 0, 1, 1, 0, 0, 1, 1], + "y": [0, 1, 1, 0, 0, 1, 1, 0], + "z": [0, 0, 0, 0, 1, 1, 1, 1], + "u": [0, 0, 2, 2, 0, 0, 2, 2], + "v": [0, 2, 2, 0, 0, 2, 2, 0], + "w": [0, 0, 0, 0, 2, 2, 2, 2], + "starts": { + "x": [0.25, 0.5, 0.75, 0.25, 0.5, 0.75, 0.25, 0.5, 0.75], + "y": [0, 0, 0, 0, 0, 0, 0, 0, 0], + "z": [0.25, 0.25, 0.25, 0.5, 0.5, 0.5, 0.75, 0.75, 0.75] + }, + "showscale": false, + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "type": "streamtube", + "name": "streamtube", + "scene": "scene8" + }, { + "showlegend": true, + "x": [0, 0, 0, 0, 1, 1, 1, 1], + "y": [0, 0, 1, 1, 0, 0, 1, 1], + "z": [0, 1, 0, 1, 0, 1, 0, 1], + "value": [-1, -1, -1, 1, 1, -1, -1, -1], + "showscale": false, + "colorscale": "Portland", + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "isomin": -0.3, + "isomax": 0.3, + "type": "isosurface", + "name": "isosurface", + "scene": "scene9" + }, { + "showlegend": true, + "x": [0, 0, 0, 0, 1, 1, 1, 1], + "y": [0, 0, 1, 1, 0, 0, 1, 1], + "z": [0, 1, 0, 1, 0, 1, 0, 1], + "value": [0, 0, 0, 1, 1, 0, 0, 0], + "showscale": false, + "lightposition": { "x": 1000, "y": 1000, "z": 0 }, + "opacityscale": "max", + "type": "volume", + "name": "volume", + "scene": "scene10" + }], + "layout": { + "margin": { + "t": 0, + "b": 0, + "l": 0, + "r": 0 + }, + "width": 1000, + "height": 500, + "legend": { + "borderwidth": 5, + "bordercolor": "gray", + "bgcolor": "orange", + "title": { + "text": "Plotly's
3-D traces
with multi-line
legend title
", + "font": { + "size": 17, + "family": "Times New Roman" + } + } + }, + "scene1": { + "domain": { + "x": [0, 0.2], + "y": [0, 0.5] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene2": { + "domain": { + "x": [0, 0.2], + "y": [0.5, 1] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene3": { + "domain": { + "x": [0.2, 0.4], + "y": [0, 0.5] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene4": { + "domain": { + "x": [0.2, 0.4], + "y": [0.5, 1] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene5": { + "domain": { + "x": [0.4, 0.6], + "y": [0, 0.5] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene6": { + "domain": { + "x": [0.4, 0.6], + "y": [0.5, 1] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene7": { + "domain": { + "x": [0.6, 0.8], + "y": [0, 0.5] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene8": { + "domain": { + "x": [0.6, 0.8], + "y": [0.5, 1] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene9": { + "domain": { + "x": [0.8, 1], + "y": [0, 0.5] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + }, + "scene10": { + "domain": { + "x": [0.8, 1], + "y": [0.5, 1] + }, + "camera": { "eye": { "x": 2, "y": -3, "z": 1 } } + } + } +} diff --git a/test/image/mocks/heatmap_legend.json b/test/image/mocks/heatmap_legend.json new file mode 100644 index 00000000000..80834953a08 --- /dev/null +++ b/test/image/mocks/heatmap_legend.json @@ -0,0 +1,123 @@ +{ + "data": [ + { + "legendgroup": "heatmap", + "showlegend": true, + "showscale": false, + "x": [0, 10, 12], + "y": [10, 12, 0], + "z": [1, 2, 3], + "zsmooth": "best", + "type": "heatmap", + "name": "heatmap" + }, + { + "legendgroup": "heatmap", + "xaxis": "x2", + "yaxis": "y2", + "showlegend": true, + "showscale": false, + "reversescale": true, + "x": [0, 10, 12], + "y": [10, 12, 0], + "z": [1, 2, 3], + "zsmooth": "best", + "type": "heatmap", + "name": "heatmap reversed scale" + }, + { + "legendgroup": "contour", + "xaxis": "x3", + "yaxis": "y3", + "showlegend": true, + "showscale": false, + "x": [0, 10, 12], + "y": [10, 12, 0], + "z": [1, 2, 3], + "contours": {"coloring": "heatmap"}, + "line": {"color": "#fff"}, + "type": "contour", + "name": "contour" + }, + { + "legendgroup": "contour", + "xaxis": "x4", + "yaxis": "y4", + "showlegend": true, + "showscale": false, + "reversescale": true, + "x": [0, 10, 12], + "y": [10, 12, 0], + "z": [1, 2, 3], + "contours": {"coloring": "heatmap"}, + "line": {"color": "#fff"}, + "type": "contour", + "name": "contour reversed scale" + } + ], + "layout": { + "legend": { + "orientation": "h", + "title": { + "side": "top left", + "text": "heatmap and contour legends" + } + }, + "height": 800, + "width": 800, + "xaxis": { + "domain": [ + 0, + 0.48 + ] + }, + "xaxis2": { + "anchor": "y2", + "domain": [ + 0.52, + 1 + ] + }, + "xaxis3": { + "anchor": "y3", + "domain": [ + 0, + 0.48 + ] + }, + "xaxis4": { + "anchor": "y4", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis": { + "domain": [ + 0, + 0.48 + ] + }, + "yaxis2": { + "anchor": "x2", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis3": { + "anchor": "x3", + "domain": [ + 0.52, + 1 + ] + }, + "yaxis4": { + "anchor": "x4", + "domain": [ + 0, + 0.48 + ] + } + } +} diff --git a/test/image/mocks/histogram2d_legend.json b/test/image/mocks/histogram2d_legend.json new file mode 100644 index 00000000000..41e1231adf9 --- /dev/null +++ b/test/image/mocks/histogram2d_legend.json @@ -0,0 +1,203 @@ +{ + "data": [ + { + "x": [ + "apples", + "apples", + "apples", + "apples", + "lemons", + "lemons" + ], + "y": [ + "red", + "red", + "yellow", + "green", + "yellow", + "yellow" + ], + "z": [ + "1", + "2", + "3", + "4", + "5", + "6" + ], + "histfunc": "sum", + "showlegend": true, + "showscale": false, + "type": "histogram2d", + "name": "histogram2d" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "x": [ + "apples", + "apples", + "apples", + "apples", + "lemons", + "lemons" + ], + "y": [ + "red", + "red", + "yellow", + "green", + "yellow", + "yellow" + ], + "z": [ + "1", + "2", + "3", + "4", + "5", + "6" + ], + "histfunc": "sum", + "showlegend": true, + "showscale": false, + "reversescale": true, + "type": "histogram2d", + "name": "histogram2d reversed scale" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "x": [ + "apples", + "apples", + "apples", + "apples", + "lemons", + "lemons" + ], + "y": [ + "red", + "red", + "yellow", + "green", + "yellow", + "yellow" + ], + "z": [ + "1", + "2", + "3", + "4", + "5", + "6" + ], + "histfunc": "sum", + "showlegend": true, + "showscale": false, + "type": "histogram2dcontour", + "name": "histogram2dcontour" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "x": [ + "apples", + "apples", + "apples", + "apples", + "lemons", + "lemons" + ], + "y": [ + "red", + "red", + "yellow", + "green", + "yellow", + "yellow" + ], + "z": [ + "1", + "2", + "3", + "4", + "5", + "6" + ], + "histfunc": "sum", + "showlegend": true, + "showscale": false, + "reversescale": true, + "type": "histogram2dcontour", + "name": "histogram2dcontour reversed scale" + } + ], + "layout": { + "legend": { + "title": { + "text": "histogram2d &
histogram2dcontour legends:", + "font": { + "family": "Times New Roman", + "size": 16 + } + } + }, + "height": 800, + "width": 800, + "xaxis": { + "domain": [ + 0, + 0.45 + ] + }, + "xaxis2": { + "anchor": "y2", + "domain": [ + 0.55, + 1 + ] + }, + "xaxis3": { + "anchor": "y3", + "domain": [ + 0, + 0.45 + ] + }, + "xaxis4": { + "anchor": "y4", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis": { + "domain": [ + 0, + 0.45 + ] + }, + "yaxis2": { + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis3": { + "anchor": "x3", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis4": { + "anchor": "x4", + "domain": [ + 0, + 0.45 + ] + } + } +} diff --git a/test/image/mocks/legend_mathjax_title_and_items.json b/test/image/mocks/legend_mathjax_title_and_items.json new file mode 100644 index 00000000000..bbe75241c69 --- /dev/null +++ b/test/image/mocks/legend_mathjax_title_and_items.json @@ -0,0 +1,52 @@ +{ +"layout": { + "legend": { + "title": { + "text": "$\\varphi ={\\frac {1+{\\sqrt {5}}}{2}}=0.618$" + }, + "orientation": "v", + "bordercolor": "gray", + "borderwidth": 5, + "bgcolor": "rgba(255,255,0,0.5)" + }, + "margin": { + "t": 25, + "b": 25, + "r": 25, + "l": 25 + }, + "width":400, + "height":300 +}, +"data": [ + { + "name": "${\\frac {1}{2}}=0.5$", + "x": [1, 2], + "y": [0.5, 0.5] + }, + { + "name": "${\\frac {2}{3}}=0.6666$", + "x": [2, 3], + "y": [0.6666, 0.6666] + }, + { + "name": "${\\frac {3}{5}}=0.6$", + "x": [3, 5], + "y": [0.6, 0.6] + }, + { + "name": "${\\frac {5}{8}}=0.6125$", + "x": [5, 8], + "y": [0.6125, 0.6125] + }, + { + "name": "${\\frac {8}{13}}=0.6153$", + "x": [8, 13], + "y": [0.6153, 0.6153] + }, + { + "name": "${\\frac {13}{21}}=0.619$", + "x": [13, 21], + "y": [0.619, 0.619] + }] +} diff --git a/test/image/mocks/legend_scroll_with_title.json b/test/image/mocks/legend_scroll_with_title.json new file mode 100644 index 00000000000..62c4eb79b0a --- /dev/null +++ b/test/image/mocks/legend_scroll_with_title.json @@ -0,0 +1,132 @@ +{ + "data": [ + { + "x": [1,2,3], + "y": [1,2,3], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [2,3,4], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [3,4,5], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [4,5,6], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [5,6,7], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [6,7,8], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [7,8,9], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [8,9,10], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [9,10,11], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [10,11,12], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [11,12,13], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [12,13,14], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [13,14,15], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [14,15,16], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [15,16,17], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [16,17,18], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [17,18,19], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [18,19,20], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [19,20,21], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [20,21,22], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [21,22,23], + "type": "scatter" + }, + { + "x": [1,2,3], + "y": [22,23,24], + "type": "bar" + }, + { + "x": [1,2,3], + "y": [23,24,25], + "type": "scatter" + } + ], + "layout": { + "legend": { + "bordercolor": "#000000", + "borderwidth": 1, + "bgcolor": "#eeffee", + "title": { + "text": "Legend title", + "font": { + "size": 20 + } + } + } + } +} diff --git a/test/image/mocks/mapbox_choropleth0-legend.json b/test/image/mocks/mapbox_choropleth0-legend.json new file mode 100644 index 00000000000..a9c007070da --- /dev/null +++ b/test/image/mocks/mapbox_choropleth0-legend.json @@ -0,0 +1,54 @@ +{ + "data": [ + { + "type": "choroplethmapbox", + "name": "NY", + "locations": ["NY"], + "z": [1], + "colorscale": [[0, "blue"], [1, "blue"]], + "showlegend": true, + "showscale": false, + "geojson": "https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/us-states.json" + }, + { + "type": "choroplethmapbox", + "name": "MA", + "locations": ["MA"], + "z": [1], + "colorscale": [[0, "orange"], [1, "orange"]], + "showlegend": true, + "showscale": false, + "geojson": "https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/us-states.json" + }, + { + "type": "choroplethmapbox", + "name": "VT", + "locations": ["VT"], + "z": [1], + "colorscale": [[0, "green"], [1, "green"]], + "showlegend": true, + "showscale": false, + "geojson": "https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/us-states.json" + } + ], + "layout": { + "mapbox": { + "center": { + "lon": -74.22, + "lat": 42.35 + }, + "zoom": 3.5 + }, + "title": { + "text": "choroplethmapbox with legends" + }, + "width": 500, + "height": 250, + "margin": { + "t": 50, + "b": 0, + "l": 0, + "r": 0 + } + } +} diff --git a/test/image/mocks/mapbox_density-mulitple_legend.json b/test/image/mocks/mapbox_density-mulitple_legend.json new file mode 100644 index 00000000000..33a2aafcd6a --- /dev/null +++ b/test/image/mocks/mapbox_density-mulitple_legend.json @@ -0,0 +1,43 @@ +{ + "data": [{ + "showlegend": true, + "showscale": false, + "type": "densitymapbox", + "name": "w/ reversescale:true", + "lon": [10, 20, 30], + "lat": [15, 25, 35], + "z": [1, 3, 2], + "zmid": 0, + "reversescale": true, + "radius": 50, + "below": "", + "colorbar": { + "y": 1, + "yanchor": "top", + "len": 0.45 + } + }, { + "showlegend": true, + "showscale": false, + "type": "densitymapbox", + "name": "w/0 z data", + "lon": [-10, -20, -30], + "lat": [15, 25, 35], + "zmin": 0, + "zauto": false, + "radius": [50, 100, 10], + "colorbar": { + "y": 0, + "yanchor": "bottom", + "len": 0.45 + } + }], + "layout": { + "mapbox": { + "style": "light", + "center": {"lat": 20} + }, + "width": 600, + "height": 400 + } +} diff --git a/test/image/mocks/mapbox_density0-legend.json b/test/image/mocks/mapbox_density0-legend.json new file mode 100644 index 00000000000..8538c332d90 --- /dev/null +++ b/test/image/mocks/mapbox_density0-legend.json @@ -0,0 +1,42 @@ +{ + "data": [{ + "type": "densitymapbox", + "name": "Montreal", + "lon": [-73.5], + "lat": [45.5], + "z": [1], + "colorscale": [[0, "blue"], [1, "blue"]], + "showscale": false, + "showlegend": true + }, { + "type": "densitymapbox", + "name": "Boston", + "lon": [-71.0], + "lat": [42.3], + "z": [1], + "colorscale": [[0, "red"], [1, "red"]], + "showscale": false, + "showlegend": true + }], + "layout": { + "mapbox": { + "style": "open-street-map", + "center": { + "lon": -72.25, + "lat": 43.9 + }, + "zoom": 4 + }, + "title": { + "text": "densitymapbox with legends" + }, + "width": 500, + "height": 250, + "margin": { + "t": 50, + "b": 0, + "l": 0, + "r": 0 + } + } +} diff --git a/test/jasmine/bundle_tests/plotschema_test.js b/test/jasmine/bundle_tests/plotschema_test.js index 89fcd888998..36149adb965 100644 --- a/test/jasmine/bundle_tests/plotschema_test.js +++ b/test/jasmine/bundle_tests/plotschema_test.js @@ -372,7 +372,7 @@ describe('plot schema', function() { var traces = Plotly.PlotSchema.get().traces; expect(traces.sankey.attributes.hoverinfo.flags.length).toBe(0); - expect(traces.choropleth.attributes.showlegend).toBe(undefined, 'no legend attrs for choropleth (for now)'); + expect(traces.parcoords.attributes.showlegend).toBe(undefined, 'no legend attrs for parcoords (for now)'); expect(traces.table.attributes.opacity).toBe(undefined, 'no opacity attr for table'); expect(traces.parcoords.attributes.hoverinfo).toBe(undefined, 'no hover attrs for parcoords'); expect(traces.scatter3d.attributes.selectedpoints).toBe(undefined, 'no selectedpoints for gl3d traces'); diff --git a/test/jasmine/tests/legend_test.js b/test/jasmine/tests/legend_test.js index 39c3ec695ba..d4e0dce0c14 100644 --- a/test/jasmine/tests/legend_test.js +++ b/test/jasmine/tests/legend_test.js @@ -172,6 +172,44 @@ describe('legend defaults', function() { expect(layoutOut.legend.orientation).toEqual('v'); }); + it('should not coerce `title.font` and `title.side` if the `title.text` is blank', function() { + var layoutWithTitle = Lib.extendDeep({ + legend: { + title: { + text: '' + } + } + }, layoutIn); + supplyLayoutDefaults(layoutWithTitle, layoutOut, []); + expect(layoutOut.legend.title.font).toEqual(undefined); + expect(layoutOut.legend.title.side).toEqual(undefined); + }); + + it('should default `title.side` to *top* for vertical legends', function() { + var layoutWithTitle = Lib.extendDeep({ + legend: { + title: { + text: 'Legend Title' + } + } + }, layoutIn); + supplyLayoutDefaults(layoutWithTitle, layoutOut, []); + expect(layoutOut.legend.title.side).toEqual('top'); + }); + + it('should default `title.side` to *left* for horizontal legends', function() { + var layoutWithTitle = Lib.extendDeep({ + legend: { + orientation: 'h', + title: { + text: 'Legend Title' + } + } + }, layoutIn); + supplyLayoutDefaults(layoutWithTitle, layoutOut, []); + expect(layoutOut.legend.title.side).toEqual('left'); + }); + describe('for horizontal legends', function() { var layoutInForHorizontalLegends; @@ -743,6 +781,42 @@ describe('legend relayout update', function() { .catch(failTest) .then(done); }); + + it('should fit in graph viewport when changing legend.title.side', function(done) { + var fig = Lib.extendDeep({}, require('@mocks/0.json')); + fig.layout.legend = { + title: { + text: 'legend title' + } + }; + + function _assert(msg, xy, wh) { + return function() { + var fullLayout = gd._fullLayout; + var legend3 = d3.select('g.legend'); + var bg3 = legend3.select('rect.bg'); + var translate = Drawing.getTranslate(legend3); + var x = translate.x; + var y = translate.y; + var w = +bg3.attr('width'); + var h = +bg3.attr('height'); + + expect([x, y]).toBeWithinArray(xy, 25, msg + '| legend x,y'); + expect([w, h]).toBeWithinArray(wh, 25, msg + '| legend w,h'); + expect(x + w <= fullLayout.width).toBe(true, msg + '| fits in x'); + expect(y + h <= fullLayout.height).toBe(true, msg + '| fits in y'); + }; + } + + Plotly.plot(gd, fig) + .then(_assert('base', [667.72, 60], [120, 83])) + .then(function() { return Plotly.relayout(gd, 'legend.title.side', 'left'); }) + .then(_assert('after relayout to *left*', [607.54, 60], [180, 67])) + .then(function() { return Plotly.relayout(gd, 'legend.title.side', 'top'); }) + .then(_assert('after relayout to *top*', [667.72, 60], [120, 83])) + .catch(failTest) + .then(done); + }); }); describe('legend orientation change:', function() {