Skip to content

Commit 11bec79

Browse files
authored
Merge pull request #3263 from plotly/2789-valign-legend
Legend Marker Vertical Alignment
2 parents 695f311 + c1b871d commit 11bec79

File tree

9 files changed

+115
-1
lines changed

9 files changed

+115
-1
lines changed

src/components/legend/attributes.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,15 @@ module.exports = {
120120
'or *bottom* of the legend.'
121121
].join(' ')
122122
},
123-
editType: 'legend'
123+
editType: 'legend',
124+
valign: {
125+
valType: 'enumerated',
126+
values: ['top', 'middle', 'bottom'],
127+
dflt: 'middle',
128+
role: 'style',
129+
editType: 'legend',
130+
description: [
131+
'Sets the vertical alignment of the symbols with respect to their associated text.',
132+
].join(' ')
133+
}
124134
};

src/components/legend/defaults.js

+1
Original file line numberDiff line numberDiff line change
@@ -103,5 +103,6 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
103103
coerce('xanchor', defaultXAnchor);
104104
coerce('y', defaultY);
105105
coerce('yanchor', defaultYAnchor);
106+
coerce('valign');
106107
Lib.noneOrAll(containerIn, containerOut, ['x', 'y']);
107108
};

src/components/legend/draw.js

+1
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ function computeTextDimensions(g, gd) {
536536
// to avoid getBoundingClientRect
537537
var textY = lineHeight * (0.3 + (1 - textLines) / 2);
538538
svgTextUtils.positionText(text, constants.textOffsetX, textY);
539+
legendItem.lineHeight = lineHeight;
539540
}
540541

541542
height = Math.max(height, 16) + 3;

src/components/legend/style.js

+13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ module.exports = function style(s, gd) {
2525
var layers = Lib.ensureSingle(traceGroup, 'g', 'layers');
2626
layers.style('opacity', d[0].trace.opacity);
2727

28+
// Marker vertical alignment
29+
var valign = gd._fullLayout.legend.valign;
30+
var lineHeight = d[0].lineHeight;
31+
var height = d[0].height;
32+
33+
if(valign === 'middle' || !lineHeight || !height) {
34+
layers.attr('transform', null); // this here is a fun d3 trick to unset DOM attributes
35+
} else {
36+
var factor = {top: 1, bottom: -1}[valign];
37+
var markerOffsetY = factor * (0.5 * (lineHeight - height + 3));
38+
layers.attr('transform', 'translate(0,' + markerOffsetY + ')');
39+
}
40+
2841
var fill = layers
2942
.selectAll('g.legendfill')
3043
.data([d]);
53.4 KB
Loading
53.4 KB
Loading
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"data": [{
3+
"y": [1, 5, 3, 4, 5],
4+
"name": "Super long name<br>Super long name<br>Super long name<br>Super long name",
5+
"type": "scatter",
6+
"showlegend": true
7+
},
8+
{
9+
"y": [3, 2, 5, 1, 5],
10+
"name": "Also super long name<br>Also super long name<br>Also super long name",
11+
"type": "scatter",
12+
"showlegend": true
13+
}
14+
],
15+
"layout": {
16+
"width": 800,
17+
"legend": {
18+
"bgcolor": "rgba(0,255,255,1)",
19+
"valign": "middle",
20+
"font": {
21+
"size": 20
22+
}
23+
}
24+
}
25+
}
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"data": [{
3+
"y": [1, 5, 3, 4, 5],
4+
"name": "Super long name<br>Super long name<br>Super long name<br>Super long name",
5+
"type": "scatter",
6+
"showlegend": true
7+
},
8+
{
9+
"y": [3, 2, 5, 1, 5],
10+
"name": "Also super long name<br>Also super long name<br>Also super long name",
11+
"type": "scatter",
12+
"showlegend": true
13+
}
14+
],
15+
"layout": {
16+
"width": 800,
17+
"legend": {
18+
"bgcolor": "rgba(0,255,255,1)",
19+
"valign": "top",
20+
"font": {
21+
"size": 20
22+
}
23+
}
24+
}
25+
}

test/jasmine/tests/legend_test.js

+39
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ var createGraphDiv = require('../assets/create_graph_div');
1616
var destroyGraphDiv = require('../assets/destroy_graph_div');
1717
var assertPlotSize = require('../assets/custom_assertions').assertPlotSize;
1818

19+
var Drawing = require('@src/components/drawing');
20+
1921
describe('legend defaults', function() {
2022
'use strict';
2123

@@ -665,6 +667,43 @@ describe('legend relayout update', function() {
665667
.catch(failTest)
666668
.then(done);
667669
});
670+
671+
describe('should update legend valign', function() {
672+
var mock = require('@mocks/legend_valign_top.json');
673+
var gd;
674+
675+
beforeEach(function() {
676+
gd = createGraphDiv();
677+
});
678+
afterEach(destroyGraphDiv);
679+
680+
function markerOffsetY() {
681+
var translate = Drawing.getTranslate(d3.select('.legend .traces .layers'));
682+
return translate.y;
683+
}
684+
685+
it('it should translate markers', function(done) {
686+
var mockCopy = Lib.extendDeep({}, mock);
687+
688+
var top, middle, bottom;
689+
Plotly.plot(gd, mockCopy.data, mockCopy.layout)
690+
.then(function() {
691+
top = markerOffsetY();
692+
return Plotly.relayout(gd, 'legend.valign', 'middle');
693+
})
694+
.then(function() {
695+
middle = markerOffsetY();
696+
expect(middle).toBeGreaterThan(top);
697+
return Plotly.relayout(gd, 'legend.valign', 'bottom');
698+
})
699+
.then(function() {
700+
bottom = markerOffsetY();
701+
expect(bottom).toBeGreaterThan(middle);
702+
})
703+
.catch(failTest)
704+
.then(done);
705+
});
706+
});
668707
});
669708

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

0 commit comments

Comments
 (0)