Skip to content

Commit 3f473b1

Browse files
add legendgroup width option
1 parent f92f786 commit 3f473b1

File tree

7 files changed

+427
-5
lines changed

7 files changed

+427
-5
lines changed

src/components/legend/attributes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ module.exports = {
7474
'Sets the amount of vertical space (in px) between legend groups.'
7575
].join(' ')
7676
},
77+
legendtextwidth: {
78+
valType: 'number',
79+
min: 0,
80+
editType: 'legend',
81+
description: 'Sets the width (in px) of the legend.',
82+
},
7783
itemsizing: {
7884
valType: 'enumerated',
7985
values: ['trace', 'constant'],

src/components/legend/defaults.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
122122
coerce('traceorder', defaultOrder);
123123
if(helpers.isGrouped(layoutOut.legend)) coerce('tracegroupgap');
124124

125+
coerce('legendtextwidth');
125126
coerce('itemsizing');
126127
coerce('itemwidth');
127128

src/components/legend/draw.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,21 @@ function _draw(gd, legendObj) {
351351
}], gd);
352352
}
353353

354+
function getTraceWidth(trace, legendObj, textGap, isGrouped) {
355+
var legendItem = trace[0];
356+
var legendWidth = legendItem.width;
357+
358+
if(legendItem.trace.legendtextwidth !== undefined) {
359+
legendWidth = legendItem.trace.legendtextwidth;
360+
} else if(isGrouped && legendItem.trace.legendgroup !== '') {
361+
legendWidth = legendItem.width;
362+
} else if(legendObj.legendtextwidth !== undefined) {
363+
legendWidth = legendObj.legendtextwidth;
364+
}
365+
366+
return legendWidth + textGap;
367+
}
368+
354369
function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
355370
var trace = legendItem.data()[0][0].trace;
356371
var evtData = {
@@ -636,6 +651,7 @@ function computeLegendDimensions(gd, groups, traces, legendObj) {
636651
var isAbovePlotArea = legendObj.y > 1 || (legendObj.y === 1 && yanchor === 'bottom');
637652

638653
var traceGroupGap = legendObj.tracegroupgap;
654+
var legendGroupWidths = {};
639655

640656
// - if below/above plot area, give it the maximum potential margin-push value
641657
// - otherwise, extend the height of the plot area
@@ -688,7 +704,7 @@ function computeLegendDimensions(gd, groups, traces, legendObj) {
688704
var maxItemWidth = 0;
689705
var combinedItemWidth = 0;
690706
traces.each(function(d) {
691-
var w = d[0].width + textGap;
707+
var w = getTraceWidth(d, legendObj, textGap, isGrouped);
692708
maxItemWidth = Math.max(maxItemWidth, w);
693709
combinedItemWidth += w;
694710
});
@@ -704,15 +720,16 @@ function computeLegendDimensions(gd, groups, traces, legendObj) {
704720
var maxWidthInGroup = 0;
705721
var offsetY = 0;
706722
d3.select(this).selectAll('g.traces').each(function(d) {
707-
var w = d[0].width;
723+
var w = getTraceWidth(d, legendObj, textGap, isGrouped);
708724
var h = d[0].height;
709725

710726
Drawing.setTranslate(this,
711727
titleSize[0],
712728
titleSize[1] + bw + itemGap + h / 2 + offsetY
713729
);
714730
offsetY += h;
715-
maxWidthInGroup = Math.max(maxWidthInGroup, textGap + w);
731+
maxWidthInGroup = Math.max(maxWidthInGroup, w);
732+
legendGroupWidths[d[0].trace.legendgroup] = maxWidthInGroup;
716733
});
717734

718735
var next = maxWidthInGroup + itemGap;
@@ -750,7 +767,7 @@ function computeLegendDimensions(gd, groups, traces, legendObj) {
750767
var rowWidth = 0;
751768
traces.each(function(d) {
752769
var h = d[0].height;
753-
var w = textGap + d[0].width;
770+
var w = getTraceWidth(d, legendObj, textGap, isGrouped);
754771
var next = (oneRowLegend ? w : maxItemWidth) + itemGap;
755772

756773
if((next + bw + offsetX - itemGap) >= legendObj._maxWidth) {
@@ -802,7 +819,12 @@ function computeLegendDimensions(gd, groups, traces, legendObj) {
802819
traces.each(function(d) {
803820
var traceToggle = d3.select(this).select('.legendtoggle');
804821
var h = d[0].height;
805-
var w = isEditable ? textGap : (toggleRectWidth || (textGap + d[0].width));
822+
var legendgroup = d[0].trace.legendgroup;
823+
var traceWidth = getTraceWidth(d, legendObj, textGap);
824+
if(isGrouped && legendgroup !== '') {
825+
traceWidth = legendGroupWidths[legendgroup];
826+
}
827+
var w = isEditable ? textGap : (toggleRectWidth || traceWidth);
806828
if(!isVertical) w += itemGap / 2;
807829
Drawing.setRect(traceToggle, 0, -h / 2, w, h);
808830
});

src/plots/attributes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ module.exports = {
7272
'and ranks greater than 1000 to go after all unranked items.'
7373
].join(' ')
7474
},
75+
legendtextwidth: {
76+
valType: 'number',
77+
min: 0,
78+
editType: 'style',
79+
description: 'Sets the width (in px) of the legend for this trace.',
80+
},
7581
opacity: {
7682
valType: 'number',
7783
min: 0,

src/plots/plots.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac
13201320
'showlegend'
13211321
);
13221322

1323+
coerce('legendtextwidth');
13231324
coerce('legendgroup');
13241325
coerce('legendgrouptitle.text');
13251326
coerce('legendrank');

test/jasmine/tests/legend_test.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var DBLCLICKDELAY = require('@src/plot_api/plot_config').dfltConfig.doubleClickD
66
var Legend = require('@src/components/legend');
77
var getLegendData = require('@src/components/legend/get_legend_data');
88
var helpers = require('@src/components/legend/helpers');
9+
var constants = require('@src/components/legend/constants');
910

1011
var d3Select = require('../../strict-d3').select;
1112
var d3SelectAll = require('../../strict-d3').selectAll;
@@ -2345,3 +2346,94 @@ describe('legend with custom doubleClickDelay', function() {
23452346
.then(done, done.fail);
23462347
}, 3 * jasmine.DEFAULT_TIMEOUT_INTERVAL);
23472348
});
2349+
2350+
describe('legend with custom legendtextwidth', function() {
2351+
var gd;
2352+
2353+
var data = [
2354+
{x: [1, 2, 1], y: [1, 2, 1], name: 'legend text 1'},
2355+
{x: [2, 1, 2], y: [2, 1, 2], name: 'legend text 12'},
2356+
{x: [2, 3, 4], y: [2, 3, 4], name: 'legend text 123'}
2357+
];
2358+
2359+
var layout = {
2360+
legend: {
2361+
orientation: 'h'
2362+
}
2363+
};
2364+
2365+
beforeEach(function() {
2366+
gd = createGraphDiv();
2367+
});
2368+
2369+
afterEach(destroyGraphDiv);
2370+
2371+
function assertLegendTextWidth(variants) {
2372+
var nodes = d3SelectAll('rect.legendtoggle');
2373+
var index = 0;
2374+
nodes.each(function() {
2375+
var node = d3Select(this);
2376+
var w = +node.attr('width');
2377+
if(variants[index]) expect(w).toEqual(variants[index]);
2378+
index += 1;
2379+
});
2380+
}
2381+
2382+
it('should change width when trace has legendtextwidth', function(done) {
2383+
var extendedData = Lib.extendDeep([], data);
2384+
extendedData.forEach(function(trace, index) {
2385+
trace.legendtextwidth = (index + 1) * 50;
2386+
});
2387+
2388+
var textGap = 30 + constants.itemGap * 2 + constants.itemGap / 2;
2389+
2390+
Plotly.newPlot(gd, {data: extendedData, layout: layout}).then(function() {
2391+
assertLegendTextWidth([50 + textGap, 100 + textGap, 150 + textGap]);
2392+
}).then(done);
2393+
});
2394+
2395+
it('should change width when legend has legendtextwidth', function(done) {
2396+
var extendedLayout = Lib.extendDeep([], layout);
2397+
var width = 50;
2398+
extendedLayout.legend.legendtextwidth = width;
2399+
2400+
var textGap = 30 + constants.itemGap * 2 + constants.itemGap / 2;
2401+
2402+
Plotly.newPlot(gd, {data: data, layout: extendedLayout}).then(function() {
2403+
assertLegendTextWidth([width + textGap, width + textGap, width + textGap]);
2404+
}).then(done);
2405+
});
2406+
2407+
it('should change group width when trace has legendtextwidth', function(done) {
2408+
var extendedLayout = Lib.extendDeep([], layout);
2409+
extendedLayout.legend.traceorder = 'grouped';
2410+
2411+
var extendedData = Lib.extendDeep([], data);
2412+
extendedData[0].legendtextwidth = 100;
2413+
extendedData[0].legendgroup = 'test';
2414+
extendedData[1].legendgroup = 'test';
2415+
2416+
var textGap = 30 + constants.itemGap * 2 + constants.itemGap / 2;
2417+
2418+
Plotly.newPlot(gd, {data: extendedData, layout: extendedLayout}).then(function() {
2419+
assertLegendTextWidth([100 + textGap, 100 + textGap, undefined]);
2420+
}).then(done);
2421+
});
2422+
2423+
it('should prefer group legendtextwidth to the legend legendtextwidth', function(done) {
2424+
var extendedLayout = Lib.extendDeep([], layout);
2425+
extendedLayout.legend.traceorder = 'grouped';
2426+
extendedLayout.legend.legendtextwidth = 50;
2427+
2428+
var extendedData = Lib.extendDeep([], data);
2429+
extendedData[0].legendtextwidth = 100;
2430+
extendedData[0].legendgroup = 'test';
2431+
extendedData[1].legendgroup = 'test';
2432+
2433+
var textGap = 30 + constants.itemGap * 2 + constants.itemGap / 2;
2434+
2435+
Plotly.newPlot(gd, {data: extendedData, layout: extendedLayout}).then(function() {
2436+
assertLegendTextWidth([100 + textGap, 100 + textGap, 50 + textGap]);
2437+
}).then(done);
2438+
});
2439+
});

0 commit comments

Comments
 (0)