Skip to content

Commit a24fea7

Browse files
authored
Merge pull request #3628 from nathanjenx/fix1913-horizontal-legened-groups
Fix #1913 by pushing overflowed legend items to a new line
2 parents 12ce725 + a5fa247 commit a24fea7

File tree

5 files changed

+128
-10
lines changed

5 files changed

+128
-10
lines changed

src/components/legend/draw.js

+43-10
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,8 @@ function computeLegendDimensions(gd, groups, traces) {
556556

557557
var extraWidth = 0;
558558

559+
var traceGap = 5;
560+
559561
opts._width = 0;
560562
opts._height = 0;
561563

@@ -589,23 +591,53 @@ function computeLegendDimensions(gd, groups, traces) {
589591
extraWidth = 40;
590592
}
591593
else if(isGrouped) {
592-
var groupXOffsets = [opts._width];
594+
var maxHeight = 0;
595+
var maxWidth = 0;
593596
var groupData = groups.data();
594597

595-
for(var i = 0, n = groupData.length; i < n; i++) {
596-
var textWidths = groupData[i].map(function(legendItemArray) {
598+
var maxItems = 0;
599+
600+
var i;
601+
for(i = 0; i < groupData.length; i++) {
602+
var group = groupData[i];
603+
var groupWidths = group.map(function(legendItemArray) {
597604
return legendItemArray[0].width;
598605
});
599606

600-
var groupWidth = 40 + Math.max.apply(null, textWidths);
607+
var groupWidth = Lib.aggNums(Math.max, null, groupWidths);
608+
var groupHeight = group.reduce(function(a, b) {
609+
return a + b[0].height;
610+
}, 0);
601611

602-
opts._width += opts.tracegroupgap + groupWidth;
612+
maxWidth = Math.max(maxWidth, groupWidth);
613+
maxHeight = Math.max(maxHeight, groupHeight);
614+
maxItems = Math.max(maxItems, group.length);
615+
}
603616

617+
maxWidth += traceGap;
618+
maxWidth += 40;
619+
620+
var groupXOffsets = [opts._width];
621+
var groupYOffsets = [];
622+
var rowNum = 0;
623+
for(i = 0; i < groupData.length; i++) {
624+
if(fullLayout._size.w < (borderwidth + opts._width + traceGap + maxWidth)) {
625+
groupXOffsets[groupXOffsets.length - 1] = groupXOffsets[0];
626+
opts._width = maxWidth;
627+
rowNum++;
628+
} else {
629+
opts._width += maxWidth + borderwidth;
630+
}
631+
632+
var rowYOffset = (rowNum * maxHeight);
633+
rowYOffset += rowNum > 0 ? opts.tracegroupgap : 0;
634+
635+
groupYOffsets.push(rowYOffset);
604636
groupXOffsets.push(opts._width);
605637
}
606638

607639
groups.each(function(d, i) {
608-
Drawing.setTranslate(this, groupXOffsets[i], 0);
640+
Drawing.setTranslate(this, groupXOffsets[i], groupYOffsets[i]);
609641
});
610642

611643
groups.each(function() {
@@ -623,11 +655,13 @@ function computeLegendDimensions(gd, groups, traces) {
623655

624656
groupHeight += textHeight;
625657
});
626-
627-
opts._height = Math.max(opts._height, groupHeight);
628658
});
629659

630-
opts._height += 10 + borderwidth * 2;
660+
var maxYLegend = groupYOffsets[groupYOffsets.length - 1] + maxHeight;
661+
opts._height = 10 + (borderwidth * 2) + maxYLegend;
662+
663+
var maxOffset = Math.max.apply(null, groupXOffsets);
664+
opts._width = maxOffset + maxWidth + 40;
631665
opts._width += borderwidth * 2;
632666
}
633667
else {
@@ -636,7 +670,6 @@ function computeLegendDimensions(gd, groups, traces) {
636670
var maxTraceWidth = 0;
637671
var offsetX = 0;
638672
var fullTracesWidth = 0;
639-
var traceGap = opts.tracegroupgap || 5;
640673

641674
// calculate largest width for traces and use for width of all legend items
642675
traces.each(function(d) {
-15 Bytes
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"data": [{
3+
"x": [1, 2, 3, 4],
4+
"y": [63.69, 62.55, 61.64, 61.39],
5+
"legendgroup": 1,
6+
"name": "Trace A-1"
7+
}, {
8+
"x": [1, 2, 3, 4],
9+
"y": [58.24, 54.93, 42.11, 50.75],
10+
"legendgroup": 1,
11+
"name":"Trace A-2"
12+
}, {
13+
"x": [1, 2, 3, 4],
14+
"y": [51.49, 49.59, 37.12, 31.45],
15+
"legendgroup":2,
16+
"name":"Trace B-1"
17+
}, {
18+
"x": [1, 2, 3, 4],
19+
"y": [49.09, 58.54, 53.91, 43.12],
20+
"legendgroup":2,
21+
"name":"Trace B-2"
22+
}, {
23+
"x": [1, 2, 3, 4],
24+
"y": [70.53, 72.51, 72.28, 78.65],
25+
"name":"Trace C-1"
26+
}, {
27+
"x": [1, 2, 3, 4],
28+
"y": [62.69, 59.09, 63.82, 62],
29+
"legendgroup":3,
30+
"name":"Trace D-1"
31+
}, {
32+
"x": [1, 2, 3, 4],
33+
"y": [76.27, 71.43, 59.83, 64.34],
34+
"legendgroup":3,
35+
"name":"Trace D-2"
36+
}, {
37+
"x": [1, 2, 3, 4],
38+
"y": [71.15, 81.82, 88.46, 74.29],
39+
"name":"Trace E-1"
40+
}],
41+
"layout": {
42+
"width": 600,
43+
"legend": {
44+
"orientation": "h"
45+
},
46+
"xaxis": {
47+
"type": "linear",
48+
"range": [0.7840236686390533, 4.215976331360947],
49+
"autorange": true
50+
},
51+
"yaxis": {
52+
"type": "linear",
53+
"range": [27.274108280254776, 92.63589171974522],
54+
"autorange": true
55+
}
56+
}
57+
}

test/jasmine/tests/legend_test.js

+28
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,34 @@ describe('legend relayout update', function() {
705705
.then(done);
706706
});
707707
});
708+
709+
describe('with legendgroup', function() {
710+
var mock = require('@mocks/legendgroup_horizontal_wrapping.json');
711+
var gd;
712+
713+
beforeEach(function() {
714+
gd = createGraphDiv();
715+
});
716+
afterEach(destroyGraphDiv);
717+
718+
it('changes the margin size to fit tracegroupgap', function(done) {
719+
var mockCopy = Lib.extendDeep({}, mock);
720+
Plotly.newPlot(gd, mockCopy)
721+
.then(function() {
722+
expect(gd._fullLayout._size.b).toBe(130);
723+
return Plotly.relayout(gd, 'legend.tracegroupgap', 70);
724+
})
725+
.then(function() {
726+
expect(gd._fullLayout._size.b).toBe(185);
727+
return Plotly.relayout(gd, 'legend.tracegroupgap', 10);
728+
})
729+
.then(function() {
730+
expect(gd._fullLayout._size.b).toBe(130);
731+
})
732+
.catch(failTest)
733+
.then(done);
734+
});
735+
});
708736
});
709737

710738
describe('legend orientation change:', function() {

0 commit comments

Comments
 (0)