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() {