You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Horizontal legend layouts with long series names become vertical #960
I've encountered an odd behavior on horizontal legends with long series names. If the series names are relatively short, setting layout.legend.orientation to 'h' does indeed produce a reasonably laid out horizontal legend where each line of the legend is filled with as many series as possible (first example in the codepen). If, however, a series has a very long name, each series is placed on its own row in the legend (effectively becoming a vertical legend), even though there's clearly room for series with shorter names to be placed on the same rows (second example in the codepen). I think the more logical behavior should be something like this, where the series are packed into rows as closely as their names allow.
I'm happy to make a PR to improve this (it should just involve fiddling with the logic near legend/draw.js:603). Should the current behavior be preserved via a new legend attribute whose default is the current behavior and has an option for the new behavior, or is the current behavior never reasonable and should be replaced?
CC to @markwatabe who originally discussed this issue with me and helped with the proposed solution.
Looks like this is in fact the intended behavior as per the discussion following #769 (comment) and implemented in #786.
In brief, the current implementation gives one width value for all legend items. That width value is maximum text width of trace name present on the graph as computed here.
I understand that the current behavior may not be ideal for all. I wouldn't be opposed to making the trace item layout you're referring to available as a new feature.
Perhaps we should add a boolean attribute wrappeditems that would be false be default. When set to turned horizontal legend items would be displayed as you described. This solution isn't perfect, as this new wrappeditems attribute would only make sense for legends with orientation: 'h' - making the learning curve a little steeper for our users.
Alternatively, we should come up with a more granular solution similar to what's proposed in #881 - where we could make the dimension of each legend item configurable.
You could also come up with different semantics at the top level besides 'v' & 'h'. For example:
horizontal
vertical
columns
To me the current implementation of horizontal is more clearly represented by the word "columns", because of the unintended consequences of spacing being tied to word length. Clear semantics is the most important aspect when creating a package with a less steep learning curve.
So given that this was intentional behavior, we should presumably avoid changing the behavior of orientation: 'h' if possible to avoid a breaking API change, but I I suppose a different option to orientation could be added: 'hw' or similar for "horizontal wrapped".
I think adding the wrappeditems attribute is reasonable. It's perhaps not optimal that it's only applicable for legends with orientation: 'h', but there are other cases of this: for instance, the axis categoryarray attribute only has an effect if categoryorder is set to "array".
If I understand how such a granular solution would work, the idea would be that each series could be set to either participate in the maximum width (the current behavior) or use its own? This would be a superset of the wrappeditems option. The attribute could be set to:
false (all series use the maximum width: current behavior). Default.
true (all series use their own width)
An array of booleans (for each element, if true the corresponding series uses its own width, and if false uses the maximum width of all series set to false)
I vote personally vote 👎 on adding a hw value to the orientation attribute. Making orientation take anything else then v for vertical and h for horizontal would be inconsistent with the other places where we use orientation such as in the bar trace type.
but there are other cases of this: for instance, the axis categoryarray attribute only has an effect if categoryorder is set to "array".
Good point here. Attributes that only have effect when an other attribute is set to a particular value aren't uncommon in plotly.js. Adding something like wrappeditems would definitively be the easiest to get this functionality out. But, I'm thinking maybe we could do better by adding a more general option that could specify other useful legend layouts.
@markwatabe made a good point about a possible 'column' attribute or value. The default vertical legend is indeed a column of legend items, but maybe we could have add 'block' option or even a columns: 2 attribute-value pair to make the legend span multiple columns. In turned, the wrappeditems functionality could be set by setting columns: 4.
Yes, I agree on the downvote for adding a hw value to the orientation attribute.
Why would columns: 4 create the wrapping like I suggested here? If anything, it seems like 0 is the logical value to create a wrapping without columns. Also, I'm not sure how the current horizontal behavior would be expressed with a column attribute, because to my understanding it (dynamically) selects the maximum number of columns it can use given the label lengths.
it seems like 0 is the logical value to create a wrapping without columns.
Oh right. That's even better than my suggestion. 👍
, I'm not sure how the current horizontal behavior would be expressed with a column attribute,
Good point here. Maybe column isn't the right word as a column's dimensions for horizontal legends is computed vary differently than for vertical legend. Maybe blocks is a better term for it.
I'm still not sure of what the correct value is for a blocks attribute to express the current behavior. 0 doesn't make sense because we'll use it for the wrapped behavior, and any number greater than or equal to 1 seems like it should produce exactly that many blocks, but the current behavior can choose the number of blocks it sees fit.
Perhaps we ought to treat this instead as a maximum number of columns, where 0 means "no maximum" (the current behavior), 1 means wrapping into a single column (the behavior we discussed adding in the first place), and greater values will only produce up to that number of columns.
Also running into this issue, and willing to create a PR but not sure what ya'll deem the best way to do this.
I'm actually having the same need as #1193 --- if there is only one row in a horizontal legend then using the max width doesn't look too pretty, BUT for me I would want to use the max width if it went to multiple lines.
wonder if this should be a separate option on the legend like "renderInline" (needs a better name) that you could force to be true for all traces. (for the case like @mboorstin's)
but then also change current functionality so that if you can fit all the traces in one row in the legend's area -- and in that case we would also ignore maxwidth. i generally think that's a better outcome for everyone, but I could be thinking about this narrowly with my use cases.
I am also running into a combination of this issue and #771 since every legend item is in its own row, the legend takes up the entire space of the graph.
I got around this by making the legend font size smaller. But I hope that this is a temporary work around.
I do not know if this is the expected behavior but when you set legends horizontally and you group some legends, the grouped legends are displayed vertically.
A community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.
Sponsorship range: $5k-$10k
What Sponsorship includes:
Completion of this feature to the Sponsor's satisfaction, in a manner coherent with the rest of the Plotly.js library and API
Tests for this feature
Long-term support (continued support of this feature in the latest version of Plotly.js)
The simplest workaround to avoid this problem is to slice(0, maxLengthForLegend) the trace's name (maxLengthForLegend between 20 and 30 seem to work well and allow the trace name to be recognized).
Hi - this issue has been sitting for a while, so as part of our effort to tidy up our public repositories I'm going to close it. If it's still a concern, we'd be grateful if you could open a new issue (with a short reproducible example if appropriate) so that we can add it to our stack. Cheers - @gvwilson
Activity
etpinard commentedon Sep 22, 2016
Looks like this is in fact the intended behavior as per the discussion following #769 (comment) and implemented in #786.
In brief, the current implementation gives one width value for all legend items. That width value is maximum text width of trace
namepresent on the graph as computed here.I understand that the current behavior may not be ideal for all. I wouldn't be opposed to making the trace item layout you're referring to available as a new feature.
Perhaps we should add a boolean attribute
wrappeditemsthat would befalsebe default. When set to turned horizontal legend items would be displayed as you described. This solution isn't perfect, as this newwrappeditemsattribute would only make sense for legends withorientation: 'h'- making the learning curve a little steeper for our users.Alternatively, we should come up with a more granular solution similar to what's proposed in #881 - where we could make the dimension of each legend item configurable.
markwatabe commentedon Sep 22, 2016
You could also come up with different semantics at the top level besides 'v' & 'h'. For example:
horizontal
vertical
columns
To me the current implementation of horizontal is more clearly represented by the word "columns", because of the unintended consequences of spacing being tied to word length. Clear semantics is the most important aspect when creating a package with a less steep learning curve.
mboorstin commentedon Sep 22, 2016
So given that this was intentional behavior, we should presumably avoid changing the behavior of
orientation: 'h'if possible to avoid a breaking API change, but I I suppose a different option toorientationcould be added:'hw'or similar for "horizontal wrapped".I think adding the
wrappeditemsattribute is reasonable. It's perhaps not optimal that it's only applicable for legends withorientation: 'h', but there are other cases of this: for instance, the axiscategoryarrayattribute only has an effect ifcategoryorderis set to"array".If I understand how such a granular solution would work, the idea would be that each series could be set to either participate in the maximum width (the current behavior) or use its own? This would be a superset of the
wrappeditemsoption. The attribute could be set to:false(all series use the maximum width: current behavior). Default.true(all series use their own width)truethe corresponding series uses its own width, and iffalseuses the maximum width of all series set tofalse)etpinard commentedon Sep 22, 2016
@mboorstin thanks very much for your input.
I vote personally vote 👎 on adding a
hwvalue to theorientationattribute. Makingorientationtake anything else then v for vertical and h for horizontal would be inconsistent with the other places where we useorientationsuch as in thebartrace type.Good point here. Attributes that only have effect when an other attribute is set to a particular value aren't uncommon in plotly.js. Adding something like
wrappeditemswould definitively be the easiest to get this functionality out. But, I'm thinking maybe we could do better by adding a more general option that could specify other useful legend layouts.@markwatabe made a good point about a possible
'column'attribute or value. The default vertical legend is indeed a column of legend items, but maybe we could have add'block'option or even acolumns: 2attribute-value pair to make the legend span multiple columns. In turned, thewrappeditemsfunctionality could be set by settingcolumns: 4.mboorstin commentedon Sep 22, 2016
Yes, I agree on the downvote for adding a
hwvalue to theorientationattribute.Why would
columns: 4create the wrapping like I suggested here? If anything, it seems like0is the logical value to create a wrapping without columns. Also, I'm not sure how the current horizontal behavior would be expressed with acolumnattribute, because to my understanding it (dynamically) selects the maximum number of columns it can use given the label lengths.etpinard commentedon Sep 22, 2016
Oh right. That's even better than my suggestion. 👍
Good point here. Maybe
columnisn't the right word as a column's dimensions for horizontal legends is computed vary differently than for vertical legend. Maybeblocksis a better term for it.mboorstin commentedon Sep 22, 2016
I'm still not sure of what the correct value is for a
blocksattribute to express the current behavior. 0 doesn't make sense because we'll use it for the wrapped behavior, and any number greater than or equal to 1 seems like it should produce exactly that many blocks, but the current behavior can choose the number of blocks it sees fit.Perhaps we ought to treat this instead as a maximum number of columns, where 0 means "no maximum" (the current behavior), 1 means wrapping into a single column (the behavior we discussed adding in the first place), and greater values will only produce up to that number of columns.
tylerlee commentedon Jun 28, 2017
Also running into this issue, and willing to create a PR but not sure what ya'll deem the best way to do this.
I'm actually having the same need as #1193 --- if there is only one row in a horizontal legend then using the max width doesn't look too pretty, BUT for me I would want to use the max width if it went to multiple lines.
wonder if this should be a separate option on the legend like "renderInline" (needs a better name) that you could force to be true for all traces. (for the case like @mboorstin's)
but then also change current functionality so that if you can fit all the traces in one row in the legend's area -- and in that case we would also ignore maxwidth. i generally think that's a better outcome for everyone, but I could be thinking about this narrowly with my use cases.
SergioCordova commentedon Jul 3, 2017
I am also running into a combination of this issue and #771 since every legend item is in its own row, the legend takes up the entire space of the graph.
I got around this by making the legend font size smaller. But I hope that this is a temporary work around.
AdnanBoota commentedon May 3, 2019
etpinard commentedon Jan 7, 2020
From @gilles-crealp 's #4394
I do not know if this is the expected behavior but when you set legends horizontally and you group some legends, the grouped legends are displayed vertically.
See traces 0 to 4 in the screen capture.
https://jsfiddle.net/0zct6ao8/
jackparmer commentedon Sep 10, 2020
This issue has been tagged with
NEEDS SPON$ORA community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.
Sponsorship range: $5k-$10k
What Sponsorship includes:
Please include the link to this issue when contacting us to discuss.
paulovieira commentedon Jun 14, 2022
The simplest workaround to avoid this problem is to
slice(0, maxLengthForLegend)the trace's name (maxLengthForLegendbetween 20 and 30 seem to work well and allow the trace name to be recognized).gvwilson commentedon Jun 5, 2024
Hi - this issue has been sitting for a while, so as part of our effort to tidy up our public repositories I'm going to close it. If it's still a concern, we'd be grateful if you could open a new issue (with a short reproducible example if appropriate) so that we can add it to our stack. Cheers - @gvwilson