Skip to content

Commit

Permalink
Merge pull request #5752 from plotly/legendgroup-titles
Browse files Browse the repository at this point in the history
Legend group titles
  • Loading branch information
archmoj authored Jun 24, 2021
2 parents 608337e + 4289532 commit dc15177
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 19 deletions.
40 changes: 30 additions & 10 deletions src/components/legend/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,20 +393,26 @@ function drawTexts(g, gd, legendObj) {
var isEditable = !legendObj._inHover && gd._context.edits.legendText && !isPieLike;
var maxNameLength = legendObj._maxNameLength;

var name;
if(!legendObj.entries) {
name = isPieLike ? legendItem.label : trace.name;
if(trace._meta) {
name = Lib.templateString(name, trace._meta);
}
var name, font;
if(legendItem.groupTitle) {
name = legendItem.groupTitle.text;
font = legendItem.groupTitle.font;
} else {
name = legendItem.text;
font = legendObj.font;
if(!legendObj.entries) {
name = isPieLike ? legendItem.label : trace.name;
if(trace._meta) {
name = Lib.templateString(name, trace._meta);
}
} else {
name = legendItem.text;
}
}

var textEl = Lib.ensureSingle(g, 'text', 'legendtext');

textEl.attr('text-anchor', 'start')
.call(Drawing.font, legendObj.font)
.call(Drawing.font, font)
.text(isEditable ? ensureLength(name, maxNameLength) : name);

var textGap = legendObj.itemwidth + constants.itemGap * 2;
Expand Down Expand Up @@ -512,7 +518,15 @@ function computeTextDimensions(g, gd, legendObj, aTitle) {
var mathjaxNode = mathjaxGroup.node();
if(!legendObj) legendObj = gd._fullLayout.legend;
var bw = legendObj.borderwidth;
var lineHeight = (aTitle === MAIN_TITLE ? legendObj.title : legendObj).font.size * LINE_SPACING;
var font;
if(aTitle === MAIN_TITLE) {
font = legendObj.title.font;
} else if(legendItem.groupTitle) {
font = legendItem.groupTitle.font;
} else {
font = legendObj.font;
}
var lineHeight = font.size * LINE_SPACING;
var height, width;

if(mathjaxNode) {
Expand Down Expand Up @@ -549,8 +563,14 @@ function computeTextDimensions(g, gd, legendObj, aTitle) {
bw + lineHeight
);
} else { // legend item
var x = constants.itemGap * 2 + legendObj.itemwidth;
if(legendItem.groupTitle) {
x = constants.itemGap;
width -= legendObj.itemwidth;
}

svgTextUtils.positionText(textEl,
legendObj.itemwidth + constants.itemGap * 2,
x,
-lineHeight * ((textLines - 1) / 2 - 0.3)
);
}
Expand Down
25 changes: 25 additions & 0 deletions src/components/legend/get_legend_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,33 @@ module.exports = function getLegendData(calcdata, opts) {
// sort considering trace.legendrank and legend.traceorder
legendData[i].forEach(function(a, k) { a._preSort = k; });
legendData[i].sort(orderFn2);

var firstItem = legendData[i][0];

var groupTitle = null;
// get group title text
for(j = 0; j < legendData[i].length; j++) {
var gt = legendData[i][j].trace.legendgrouptitle;
if(gt && gt.text) {
groupTitle = gt;
break;
}
}

// reverse order
if(reversed) legendData[i].reverse();

if(groupTitle) {
// set group title text
legendData[i].unshift({
i: -1,
groupTitle: groupTitle,
trace: {
showlegend: firstItem.trace.showlegend
}
});
}

// rearrange lgroupToTraces into a d3-friendly array of arrays
for(j = 0; j < legendData[i].length; j++) {
legendData[i][j] = [
Expand Down
2 changes: 2 additions & 0 deletions src/components/legend/handle_click.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ module.exports = function handleClick(g, gd, numClicks) {
[];

var legendItem = g.data()[0][0];
if(legendItem.groupTitle) return; // no click on group legends for now

var fullData = gd._fullData;
var fullTrace = legendItem.trace;
var legendgroup = fullTrace.legendgroup;
Expand Down
18 changes: 18 additions & 0 deletions src/plots/attributes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

var fontAttrs = require('./font_attributes');
var fxAttrs = require('../components/fx/attributes');

module.exports = {
Expand Down Expand Up @@ -41,6 +42,23 @@ module.exports = {
'when toggling legend items.'
].join(' ')
},
legendgrouptitle: {
text: {
valType: 'string',
dflt: '',
editType: 'style',
description: [
'Sets the title of the legend group.'
].join(' ')
},
font: fontAttrs({
editType: 'style',
description: [
'Sets this legend group\'s title font.'
].join(' '),
}),
editType: 'style',
},
legendrank: {
valType: 'number',
dflt: 1000,
Expand Down
7 changes: 7 additions & 0 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,13 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
);

coerce('legendgroup');
var titleText = coerce('legendgrouptitle.text');
if(titleText) {
Lib.coerceFont(coerce, 'legendgrouptitle.font', Lib.extendFlat({}, layout.font, {
size: Math.round(layout.font.size * 1.1) // default to larger font size
}));
}

coerce('legendrank');

traceOut._dfltShowLegend = true;
Expand Down
Binary file added test/image/baselines/legendgroup-titles-pie.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/legendgroup-titles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/legendrank.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/legendrank2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions test/image/mocks/legendgroup-titles-pie.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"data": [
{
"type": "pie",
"domain": {
"y": [0.8, 1]
},
"labels": [0],
"legendgroup": "G1"
},
{
"type": "pie",
"domain": {
"y": [0.6, 0.8]
},
"labels": [1],
"legendgroup": "G1"
},
{
"type": "pie",
"domain": {
"y": [0.4, 0.6]
},
"labels": [2],
"legendgroup": "G1",
"legendgrouptitle": {
"text": "First group"
}
},
{
"type": "pie",
"domain": {
"y": [0.2, 0.4]
},
"labels": [3],
"legendgroup": "G2",
"legendgrouptitle": {
"text": "Second group"
}
},
{
"domain": {
"y": [0, 0.2]
},
"type": "pie",
"labels": [4, 5],
"legendgroup": "G3",
"legendgrouptitle": {
"text": "Third group"
}
}
],
"layout": {
"title": {
"text": "legend group titles"
},
"margin": {
"t": 50,
"b": 25,
"l": 25,
"r": 25
},
"width": 300,
"height": 300,
"legend": {
"bgcolor": "lightblue",
"title": {
"text": ""
}
},
"hovermode": "x unified"
}
}
64 changes: 64 additions & 0 deletions test/image/mocks/legendgroup-titles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"data": [
{
"y": [0],
"legendgroup": "G1"
},
{
"y": [1],
"legendgroup": "G1"
},
{
"y": [2],
"legendgroup": "G1",
"legendgrouptitle": {
"text": "First group"
}
},
{
"y": [3],
"legendgroup": "G2",
"legendgrouptitle": {
"text": "Second<br>group",
"font": {
"family": "Raleway",
"size": 14
}
}
},
{
"y": [4],
"legendgroup": "G3",
"legendgrouptitle": {
"text": "Third group"
}
},
{
"y": [5],
"legendgroup": "G3"
}
],
"layout": {
"title": {
"text": "legend group titles"
},
"margin": {
"t": 50,
"b": 25,
"l": 25,
"r": 25
},
"width": 300,
"height": 300,
"yaxis": {
"autorange": "reversed"
},
"legend": {
"bgcolor": "lightblue",
"title": {
"text": ""
}
},
"hovermode": "x unified"
}
}
11 changes: 7 additions & 4 deletions test/image/mocks/legendrank.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{
"legendrank": 2,
"legendgroup": "pie",
"legendgrouptitle": { "text": "Next pie" },
"type": "pie",
"labels": ["a","b","c","c","c","a"],
"textinfo": "none",
Expand All @@ -15,6 +16,7 @@
{
"legendrank": 1,
"legendgroup": "pie",
"legendgrouptitle": { "text": "Pie" },
"type": "pie",
"labels": ["z","x","x","x","y", "y"],
"sort": false,
Expand All @@ -25,9 +27,9 @@
}
},
{"type": "scatter", "name": "2", "y": [2], "yaxis": "y", "legendgroup": "one", "legendrank": 2},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3},
{"type": "scatter", "name": "1", "y": [1], "yaxis": "y", "legendgroup": "one", "legendrank": 1, "legendgrouptitle": { "text": "Down" }},
{"type": "bar", "name": "2", "y": [2], "yaxis": "y2", "legendgroup": "two", "legendrank": 2, "legendgrouptitle": { "text": "Up" }},
{"type": "scatter", "name": "3", "y": [3], "yaxis": "y", "legendgroup": "one", "legendrank": 3, "legendgrouptitle": { "text": "IGNORE ME!" }},
{"type": "bar", "name": "3", "y": [3], "yaxis": "y2", "legendgroup": "two", "legendrank": 1}
],
"layout": {
Expand All @@ -36,7 +38,8 @@
},
"hovermode": "x unified",
"margin": {
"t": 50
"t": 50,
"b": 25
},
"width": 300,
"height": 400,
Expand Down
15 changes: 10 additions & 5 deletions test/image/mocks/legendrank2.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,36 @@
{
"name": "A",
"legendrank": 2,
"y": [-2]
"y": [-2],
"legendgrouptitle": { "text": "Middle" }
},
{
"name": "D",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
"legendgroup": "bottom",
"legendgrouptitle": { "text": "Bottom" }
},
{
"name": "E",
"legendrank": 4,
"y": [-4],
"legendgroup": "bottom"
"legendgroup": "bottom",
"legendgrouptitle": { "text": "Second" }
},
{
"name": "B",
"legendrank": 1,
"y": [-1],
"legendgroup": "top"
"legendgroup": "top",
"legendgrouptitle": { "text": "Top" }
},
{
"name": "C",
"legendrank": 3,
"y": [-3],
"legendgroup": "top"
"legendgroup": "top",
"legendgrouptitle": { "text": "Not first" }
}
],
"layout": {
Expand Down

0 comments on commit dc15177

Please sign in to comment.