-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Legend Marker Vertical Alignment #3263
Conversation
src/components/legend/style.js
Outdated
var valignFactor = 0; | ||
if(gd._fullLayout.legend.valign === 'top') valignFactor = 1.0; | ||
if(gd._fullLayout.legend.valign === 'bottom') valignFactor = -1.0; | ||
var markerOffsetY = valignFactor * (0.5 * (d[0].lineHeight - d[0].height + 3)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice use of lineHeight
🎉
test/image/mocks/legend_valign.json
Outdated
"width": 800, | ||
"legend": { | ||
"bgcolor": "rgba(0,255,255,1)", | ||
"valign": "top", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add another mock testing valign: "middle"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely! Done in a02910a
valType: 'enumerated', | ||
values: ['top', 'middle', 'bottom'], | ||
dflt: 'middle', | ||
role: 'style', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and let's add one jasmine test 🔒 ing down relayout
for 'valign'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch! Thank you so much! Test can be found in a375c99
src/components/legend/style.js
Outdated
@@ -30,7 +30,7 @@ module.exports = function style(s, gd) { | |||
if(gd._fullLayout.legend.valign === 'top') valignFactor = 1.0; | |||
if(gd._fullLayout.legend.valign === 'bottom') valignFactor = -1.0; | |||
var markerOffsetY = valignFactor * (0.5 * (d[0].lineHeight - d[0].height + 3)); | |||
if(markerOffsetY) layers.attr('transform', 'translate(0,' + markerOffsetY + ')'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix bug when valign
equals middle
. Because markerOffsetY
is zero when at middle
position, I had to change this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. But 'middle'
is the default value isn't it? Why didn't our tests catch sooner?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the mocks were rendered properly because the transform
attribute was not added. However, if there's already a transform
attribute on the svg group because valign
was top
, then it needs to be removed on relayout
if valign
is switched back to middle
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, if there's already a transform attribute on the svg group because valign was top, then it needs to be removed on relayout if valign is switched back to middle.
OK this makes sense. I don't understand how the transform
gets removed and why this patch fixes the bug though. Boolean(NaN)
is false
so the old and this new line look equivalent to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't something like
var valign = gd._fullLayout.valign;
if(valign === 'middle') {
layers.attr('transform', null); // this here is a fun d3 trick to unset DOM attributes
} else {
var factor = {top: 1, bottom: -1}[valign];
var markerOffsetY = factor * (0.5 * (d[0].lineHeight - d[0].height + 3));
layers.attr('transform', 'translate(0,' + markerOffsetY + ')');
}
be what we're looking for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your solution would work except that we should check whether markerOffsetY
is a number. It might not be when this function is visited and errors would be thrown in the console because of garbage translate(0, NaN)
. This is why I added if(!isNaN(markerOffsetY))
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd have to check to make sure but I think it's called on first draw to figure out how big the legend will be. At this point, d[0].lineHeight
d[0].height
are not defined and we get a NaN
. Although everything renders correctly in the end, we get the following in the console:
d3.js:663 Error: <g> attribute transform: Trailing garbage, "translate(0,NaN)".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is indeed what is happening which is why if(!isNaN(markerOffsetY))
is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, in this case if we know that lineHeight
/height
are sometimes undefined, we should not even attempt to compute markerOffsetY
. So something like:
var valign = gd._fullLayout.valign;
var lineHeight = d[0].lineHeight;
var height = d[0].height;
if(valign === 'middle' || !lineHeigth || !lheight) {
layers.attr('transform', null); // this here is a fun d3 trick to unset DOM attributes
} else {
var factor = {top: 1, bottom: -1}[valign];
var markerOffsetY = factor * (0.5 * lineHeight - height + 3));
layers.attr('transform', 'translate(0,' + markerOffsetY + ')');
}
would make that clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit c1b871d, I reused most of the above 😄 !
test/jasmine/tests/legend_test.js
Outdated
afterEach(destroyGraphDiv); | ||
|
||
function markerOffsetY() { | ||
var translateY = d3.select('.legend .traces .layers').attr('transform').match(/translate\(\d+,(-?\d+)\)/)[1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed this. Using Drawing.getTranslate
would be cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! 🔍
Done in c1b871d!
Nicely done 💃 |
Closes #2798 (never mind the issue number in the branch's name which is wrong!)