From ade0021bf3d42027ba7e7c9719aef0cc7391ee78 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 21 Jul 2025 17:29:55 -0600 Subject: [PATCH 01/14] Update maxheight calculation logic --- src/components/legend/defaults.js | 2 +- src/components/legend/draw.js | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/legend/defaults.js b/src/components/legend/defaults.js index fa292be02e7..4c98679be8e 100644 --- a/src/components/legend/defaults.js +++ b/src/components/legend/defaults.js @@ -188,7 +188,7 @@ function groupDefaults(legendId, layoutIn, layoutOut, fullData) { coerce('xanchor', defaultXAnchor); coerce('yanchor', defaultYAnchor); - coerce('maxheight', isHorizontal ? 0.5 : 1); + coerce('maxheight'); coerce('valign'); Lib.noneOrAll(containerIn, containerOut, ['x', 'y']); diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index d29dd859f44..e250ad55a22 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -769,8 +769,12 @@ function computeLegendDimensions(gd, groups, traces, legendObj) { var traceGroupGap = legendObj.tracegroupgap; var legendGroupWidths = {}; - var { maxheight, orientation, yref } = legendObj; - var heightToBeScaled = orientation === "v" && yref === "paper" ? gs.h : fullLayout.height; + const { orientation, yref } = legendObj; + let { maxheight } = legendObj; + const useFullLayoutHeight = isBelowPlotArea || isAbovePlotArea && !(orientation === "v" && yref === "paper") + // Set default maxheight here since it depends on values passed in by user + maxheight ||= useFullLayoutHeight ? 0.5 : 1; + const heightToBeScaled = useFullLayoutHeight ? fullLayout.height : gs.h; legendObj._maxHeight = Math.max(maxheight > 1 ? maxheight : maxheight * heightToBeScaled, 30); var toggleRectWidth = 0; From a7a95281c94363c7da19ae2bcc7fbbbf98d36bfd Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 21 Jul 2025 17:30:05 -0600 Subject: [PATCH 02/14] Update attribute description --- src/components/legend/attributes.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index bd897937339..5723ba1049b 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -41,8 +41,9 @@ module.exports = { 'Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than one.', 'Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px.', 'For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar.', - 'The reference height is the full layout height except for vertically oriented legends with', - 'a `yref` of `"paper"`, where the reference height is the plot height.' + 'The reference height is the full layout height with the following exception: vertically oriented legends with', + 'a `yref` of `"paper"`, unless the legend is located above/below the plot. In this case, the reference height', + 'is the plot height.' ].join(' ') }, borderwidth: { @@ -243,7 +244,7 @@ module.exports = { values: ['auto', 'top', 'middle', 'bottom'], editType: 'legend', description: [ - 'Sets the legend\'s vertical position anchor', + 'Sets the legend\'s vertical position anchor.', 'This anchor binds the `y` position to the *top*, *middle*', 'or *bottom* of the legend.', 'Value *auto* anchors legends at their bottom for `y` values less than or equal to 1/3,', From 63412926e5ac734ca5c83cc2b5c244269ebe2914 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 21 Jul 2025 17:31:55 -0600 Subject: [PATCH 03/14] Add configuration for test mock --- .../mocks/zz-legend-vertical-maxheight.json | 358 ++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 test/image/mocks/zz-legend-vertical-maxheight.json diff --git a/test/image/mocks/zz-legend-vertical-maxheight.json b/test/image/mocks/zz-legend-vertical-maxheight.json new file mode 100644 index 00000000000..1310129d644 --- /dev/null +++ b/test/image/mocks/zz-legend-vertical-maxheight.json @@ -0,0 +1,358 @@ +{ + "data": [ + { + "x": [1, 2, 3, 4], + "y": [63.69, 62.55, 61.64, 61.39], + "type": "scatter", + "mode": "lines", + "name": "Average", + "line": { "color": "rgba(0,0,0,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [58.24, 54.93, 42.11, 50.75], + "type": "scatter", + "mode": "lines", + "name": "African American", + "line": { "color": "rgba(248,118,109,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [51.49, 49.59, 37.12, 31.45], + "type": "scatter", + "mode": "lines", + "name": "Alaska Native", + "line": { "color": "rgba(216,144,0,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [49.09, 58.54, 53.91, 43.12], + "type": "scatter", + "mode": "lines", + "name": "Alaska Native Multirace", + "line": { "color": "rgba(163,165,0,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [70.53, 72.51, 72.28, 78.65], + "type": "scatter", + "mode": "lines", + "name": "Asian", + "line": { "color": "rgba(57,182,0,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [62.69, 59.09, 63.82, 62], + "type": "scatter", + "mode": "lines", + "name": "Hispanic", + "line": { "color": "rgba(0,191,125,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [76.27, 71.43, 59.83, 64.34], + "type": "scatter", + "mode": "lines", + "name": "Non Hispanic Multirace", + "line": { "color": "rgba(0,191,196,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [71.15, 81.82, 88.46, 74.29], + "type": "scatter", + "mode": "lines", + "name": "Non Resident Alien", + "line": { "color": "rgba(0,176,246,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [57.89, 57.38, 52.08, 63.83], + "type": "scatter", + "mode": "lines", + "name": "Unknown", + "line": { "color": "rgba(149,144,255,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [65.4, 63.27, 65.78, 64.03], + "type": "scatter", + "mode": "lines", + "name": "White", + "line": { "color": "rgba(231,107,243,1)" } + }, + { + "x": [1, 2, 3, 4], + "y": [63.69, 62.55, 61.64, 61.39], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(0,0,0,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(0,0,0,1)" } + }, + "name": "Average" + }, + { + "x": [1, 2, 3, 4], + "y": [58.24, 54.93, 42.11, 50.75], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(248,118,109,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(248,118,109,1)" } + }, + "name": "African American" + }, + { + "x": [1, 2, 3, 4], + "y": [51.49, 49.59, 37.12, 31.45], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(216,144,0,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(216,144,0,1)" } + }, + "name": "Alaska Native" + }, + { + "x": [1, 2, 3, 4], + "y": [49.09, 58.54, 53.91, 43.12], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(163,165,0,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(163,165,0,1)" } + }, + "name": "Alaska Native Multirace" + }, + { + "x": [1, 2, 3, 4], + "y": [70.53, 72.51, 72.28, 78.65], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(57,182,0,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(57,182,0,1)" } + }, + "name": "Asian" + }, + { + "x": [1, 2, 3, 4], + "y": [62.69, 59.09, 63.82, 62], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(0,191,125,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(0,191,125,1)" } + }, + "name": "Hispanic" + }, + { + "x": [1, 2, 3, 4], + "y": [76.27, 71.43, 59.83, 64.34], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(0,191,196,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(0,191,196,1)" } + }, + "name": "Non Hispanic Multirace" + }, + { + "x": [1, 2, 3, 4], + "y": [71.15, 81.82, 88.46, 74.29], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(0,176,246,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(0,176,246,1)" } + }, + "name": "Non Resident Alien" + }, + { + "x": [1, 2, 3, 4], + "y": [57.89, 57.38, 52.08, 63.83], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(149,144,255,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(149,144,255,1)" } + }, + "name": "Unknown" + }, + { + "x": [1, 2, 3, 4], + "y": [65.4, 63.27, 65.78, 64.03], + "type": "scatter", + "mode": "markers", + "marker": { + "autocolorscale": false, + "color": "rgba(231,107,243,1)", + "size": 5.6693, + "symbol": "circle", + "line": { "color": "rgba(231,107,243,1)" } + }, + "name": "White" + } + ], + "layout": { + "margin": { + "t": 34.8976, + "r": 75.5906, + "b": 51.243, + "l": 57.6191 + }, + "plot_bgcolor": "rgba(255,255,255,1)", + "paper_bgcolor": "rgba(255,255,255,1)", + "font": { + "color": "rgba(34,34,34,1)", + "family": "Roboto", + "size": 15.9402 + }, + "xaxis": { + "type": "linear", + "autorange": false, + "tickmode": "array", + "range": [0.4, 4.6], + "minor": { + "tickvals": [1.5, 2.5, 3.5], + "ticks": "inside", + "tickwidth": 100, + "ticklen": 5, + "tickcolor": "orange", + "gridcolor": "yellow", + "gridwidth": 100, + "griddash": "5px" + }, + "ticktext": ["2012", "2013", "2014", "2015"], + "tickvals": [1, 2, 3, 4], + "ticks": "", + "tickcolor": null, + "ticklen": 3.653, + "tickwidth": 0, + "showticklabels": true, + "tickfont": { + "color": "rgba(77,77,77,1)", + "family": "Roboto", + "size": 12.7522 + }, + "tickangle": -0, + "showline": false, + "linecolor": null, + "linewidth": 0, + "showgrid": true, + "domain": [0, 1], + "gridcolor": "rgba(216,216,216,1)", + "gridwidth": 0.6642, + "zeroline": false, + "anchor": "y", + "title": { + "text": "Fiscal year", + "font": { + "color": "rgba(34,34,34,1)", + "family": "Roboto", + "size": 15.9402 + } + }, + "hoverformat": ".2f" + }, + "yaxis": { + "type": "linear", + "autorange": false, + "tickmode": "array", + "range": [-5, 105], + "ticktext": ["0", "25", "50", "75", "100"], + "tickvals": [0, 25, 50, 75, 100], + "ticks": "", + "tickcolor": null, + "ticklen": 3.653, + "tickwidth": 0, + "showticklabels": true, + "tickfont": { + "color": "rgba(77,77,77,1)", + "family": "Roboto", + "size": 12.7522 + }, + "tickangle": -0, + "showline": false, + "linecolor": null, + "linewidth": 0, + "showgrid": true, + "domain": [0, 1], + "gridcolor": "rgba(216,216,216,1)", + "gridwidth": 0.6642, + "zeroline": false, + "anchor": "x", + "title": { + "text": "% of students", + "font": { + "color": "rgba(34,34,34,1)", + "family": "Roboto", + "size": 15.9402 + } + }, + "hoverformat": ".2f" + }, + "shapes": [ + { + "type": "rect", + "fillcolor": "transparent", + "line": { + "color": "rgba(0,0,0,1)", + "width": 0, + "linetype": "solid" + }, + "yref": "paper", + "xref": "paper", + "x0": 0, + "x1": 1, + "y0": 0, + "y1": 1 + } + ], + "showlegend": true, + "legend": { + "orientation": "v", + "x": 0.5, + "y": -0.4, + "xanchor": "center", + "yanchor": "top", + "bgcolor": "rgba(255,255,255,0.9)", + "bordercolor": "#ccc", + "borderwidth": 1, + "font": { "size": 10 }, + "itemsizing": "constant", + "itemwidth": 30, + "itemclick": "toggle", + "itemdoubleclick": "toggleothers", + "maxheight": 0.3 + }, + "hovermode": "closest" + } +} From 5ec9c40ac29f5adb1121ac69ebe608c3e80b6ef4 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 22 Jul 2025 10:30:31 -0600 Subject: [PATCH 04/14] Use different mock config --- .../mocks/zz-legend-vertical-maxheight.json | 588 +++++++++--------- 1 file changed, 291 insertions(+), 297 deletions(-) diff --git a/test/image/mocks/zz-legend-vertical-maxheight.json b/test/image/mocks/zz-legend-vertical-maxheight.json index 1310129d644..48acb700774 100644 --- a/test/image/mocks/zz-legend-vertical-maxheight.json +++ b/test/image/mocks/zz-legend-vertical-maxheight.json @@ -1,342 +1,333 @@ { + "responsive": true, + "displayModeBar": true, + "displaylogo": false, + "modeBarButtonsToRemove": ["pan2d", "lasso2d", "select2d"], "data": [ { - "x": [1, 2, 3, 4], - "y": [63.69, 62.55, 61.64, 61.39], - "type": "scatter", - "mode": "lines", - "name": "Average", - "line": { "color": "rgba(0,0,0,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "mode": "lines+markers", + "name": "Linear", + "line": { "color": "#1f77b4", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [58.24, 54.93, 42.11, 50.75], - "type": "scatter", - "mode": "lines", - "name": "African American", - "line": { "color": "rgba(248,118,109,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [1, 4, 9, 16, 25, 36, 49, 64, 81, 100], + "mode": "lines+markers", + "name": "Quadratic", + "line": { "color": "#ff7f0e", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [51.49, 49.59, 37.12, 31.45], - "type": "scatter", - "mode": "lines", - "name": "Alaska Native", - "line": { "color": "rgba(216,144,0,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000], + "mode": "lines+markers", + "name": "Cubic", + "line": { "color": "#2ca02c", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [49.09, 58.54, 53.91, 43.12], - "type": "scatter", - "mode": "lines", - "name": "Alaska Native Multirace", - "line": { "color": "rgba(163,165,0,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 1.5, 2.25, 3.375, 5.0625, 7.59375, 11.390625, 17.0859375, 25.62890625, + 38.443359375, 57.6650390625 + ], + "mode": "lines+markers", + "name": "Exponential", + "line": { "color": "#d62728", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [70.53, 72.51, 72.28, 78.65], - "type": "scatter", - "mode": "lines", - "name": "Asian", - "line": { "color": "rgba(57,182,0,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 1, 1.6931471805599454, 2.0986122886681096, 2.386294361119891, + 2.6094379124341005, 2.791759469228055, 2.9459101490553135, + 3.0794415416798357, 3.1972245773362196, 3.302585092994046 + ], + "mode": "lines+markers", + "name": "Logarithmic", + "line": { "color": "#9467bd", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [62.69, 59.09, 63.82, 62], - "type": "scatter", - "mode": "lines", - "name": "Hispanic", - "line": { "color": "rgba(0,191,125,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 12.397127693021016, 14.207354924039482, 14.987474933020273, + 14.546487134128409, 12.992360720519782, 10.705600040299336, 8.2460838615519, + 6.215987523460359, 5.112349411674515, 5.205378626684308 + ], + "mode": "lines+markers", + "name": "Sine", + "line": { "color": "#8c564b", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [76.27, 71.43, 59.83, 64.34], - "type": "scatter", - "mode": "lines", - "name": "Non Hispanic Multirace", - "line": { "color": "rgba(0,191,196,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 14.387912809451864, 12.7015115293407, 10.353686008338515, 7.919265817264288, + 5.994281922265332, 5.050037516997773, 5.317716563546019, 6.731781895681941, + 8.946021002846102, 11.418310927316131 + ], + "mode": "lines+markers", + "name": "Cosine", + "line": { "color": "#e377c2", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [71.15, 81.82, 88.46, 74.29], - "type": "scatter", - "mode": "lines", - "name": "Non Resident Alien", - "line": { "color": "rgba(0,176,246,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 10.618672499219247, 11.368273616683386, 12.520316435100678, + 15.144303244252637, 38.20283989434344, 1.4274766507438663, + 6.580306914190985, 8.167971420653178, 9.054544741793924, 9.714906913851445 + ], + "mode": "lines+markers", + "name": "Tangent", + "line": { "color": "#7f7f7f", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [57.89, 57.38, 52.08, 63.83], - "type": "scatter", - "mode": "lines", - "name": "Unknown", - "line": { "color": "rgba(149,144,255,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 3, 4.242640687119286, 5.196152422706632, 6, 6.708203932499369, + 7.348469228349534, 7.937253933193772, 8.485281374238571, 9, + 9.486832980505138 + ], + "mode": "lines+markers", + "name": "Square Root", + "line": { "color": "#bcbd22", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [65.4, 63.27, 65.78, 64.03], - "type": "scatter", - "mode": "lines", - "name": "White", - "line": { "color": "rgba(231,107,243,1)" } + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [0.01, 0.16, 0.81, 2.56, 6.25, 12.96, 24.01, 40.96, 65.61, 100], + "mode": "lines+markers", + "name": "Power 4", + "line": { "color": "#17becf", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [63.69, 62.55, 61.64, 61.39], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(0,0,0,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(0,0,0,1)" } - }, - "name": "Average" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [0.001, 0.032, 0.243, 1.024, 3.125, 7.776, 16.807, 32.768, 59.049, 100], + "mode": "lines+markers", + "name": "Power 5", + "line": { "color": "#a6cee3", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [58.24, 54.93, 42.11, 50.75], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(248,118,109,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(248,118,109,1)" } - }, - "name": "African American" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 0.0001, 0.0064, 0.0729, 0.4096, 1.5625, 4.6656, 11.7649, 26.2144, 53.1441, + 100 + ], + "mode": "lines+markers", + "name": "Power 6", + "line": { "color": "#fb9a99", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [51.49, 49.59, 37.12, 31.45], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(216,144,0,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(216,144,0,1)" } - }, - "name": "Alaska Native" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 20, 10, 6.666666666666667, 5, 4, 3.3333333333333335, 2.857142857142857, 2.5, + 2.2222222222222223, 2 + ], + "mode": "lines+markers", + "name": "Inverse", + "line": { "color": "#fdbf6f", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [49.09, 58.54, 53.91, 43.12], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(163,165,0,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(163,165,0,1)" } - }, - "name": "Alaska Native Multirace" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 11, 10.5, 10.333333333333334, 10.25, 10.2, 10.166666666666666, + 10.142857142857142, 10.125, 10.11111111111111, 10.1 + ], + "mode": "lines+markers", + "name": "Reciprocal", + "line": { "color": "#cab2d6", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [70.53, 72.51, 72.28, 78.65], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(57,182,0,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(57,182,0,1)" } - }, - "name": "Asian" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [6, 5, 4, 3, 2, 3, 4, 5, 6, 7], + "mode": "lines+markers", + "name": "Absolute", + "line": { "color": "#ffff99", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [62.69, 59.09, 63.82, 62], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(0,191,125,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(0,191,125,1)" } - }, - "name": "Hispanic" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [0, 2, 2, 4, 4, 6, 6, 8, 8, 10], + "mode": "lines+markers", + "name": "Floor", + "line": { "color": "#b15928", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [76.27, 71.43, 59.83, 64.34], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(0,191,196,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(0,191,196,1)" } - }, - "name": "Non Hispanic Multirace" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [2, 2, 4, 4, 6, 6, 8, 8, 10, 10], + "mode": "lines+markers", + "name": "Ceiling", + "line": { "color": "#fdb462", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [71.15, 81.82, 88.46, 74.29], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(0,176,246,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(0,176,246,1)" } - }, - "name": "Non Resident Alien" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [9, 8, 9, 8, 9, 8, 9, 8, 9, 8], + "mode": "lines+markers", + "name": "Modulo 2", + "line": { "color": "#b3de69", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [57.89, 57.38, 52.08, 63.83], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(149,144,255,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(149,144,255,1)" } - }, - "name": "Unknown" + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [8, 9, 7, 8, 9, 7, 8, 9, 7, 8], + "mode": "lines+markers", + "name": "Modulo 3", + "line": { "color": "#fccde5", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, { - "x": [1, 2, 3, 4], - "y": [65.4, 63.27, 65.78, 64.03], - "type": "scatter", - "mode": "markers", - "marker": { - "autocolorscale": false, - "color": "rgba(231,107,243,1)", - "size": 5.6693, - "symbol": "circle", - "line": { "color": "rgba(231,107,243,1)" } - }, - "name": "White" - } - ], - "layout": { - "margin": { - "t": 34.8976, - "r": 75.5906, - "b": 51.243, - "l": 57.6191 + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [6, 7, 8, 9, 5, 6, 7, 8, 9, 5], + "mode": "lines+markers", + "name": "Modulo 5", + "line": { "color": "#d9d9d9", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 + }, + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [0.1, 0.2, 0.6, 2.4, 12, 12, 12, 12, 12, 12], + "mode": "lines+markers", + "name": "Factorial", + "line": { "color": "#ff9896", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 + }, + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [1, 2, 6, 8, 10, 12, 14, 15, 15, 15], + "mode": "lines+markers", + "name": "Fibonacci", + "line": { "color": "#c5b0d5", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 + }, + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 1.2, 1.44, 1.728, 2.0736, 2.4883199999999994, 2.9859839999999993, + 3.583180799999999, 4.2998169599999985, 5.1597803519999985, 6.191736422399997 + ], + "mode": "lines+markers", + "name": "Geometric", + "line": { "color": "#c49c94", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, - "plot_bgcolor": "rgba(255,255,255,1)", - "paper_bgcolor": "rgba(255,255,255,1)", - "font": { - "color": "rgba(34,34,34,1)", - "family": "Roboto", - "size": 15.9402 + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [5, 7, 9, 11, 13, 15, 17, 19, 21, 23], + "mode": "lines+markers", + "name": "Arithmetic", + "line": { "color": "#f7b6d2", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, - "xaxis": { - "type": "linear", - "autorange": false, - "tickmode": "array", - "range": [0.4, 4.6], - "minor": { - "tickvals": [1.5, 2.5, 3.5], - "ticks": "inside", - "tickwidth": 100, - "ticklen": 5, - "tickcolor": "orange", - "gridcolor": "yellow", - "gridwidth": 100, - "griddash": "5px" - }, - "ticktext": ["2012", "2013", "2014", "2015"], - "tickvals": [1, 2, 3, 4], - "ticks": "", - "tickcolor": null, - "ticklen": 3.653, - "tickwidth": 0, - "showticklabels": true, - "tickfont": { - "color": "rgba(77,77,77,1)", - "family": "Roboto", - "size": 12.7522 - }, - "tickangle": -0, - "showline": false, - "linecolor": null, - "linewidth": 0, - "showgrid": true, - "domain": [0, 1], - "gridcolor": "rgba(216,216,216,1)", - "gridwidth": 0.6642, - "zeroline": false, - "anchor": "y", - "title": { - "text": "Fiscal year", - "font": { - "color": "rgba(34,34,34,1)", - "family": "Roboto", - "size": 15.9402 - } - }, - "hoverformat": ".2f" + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 10, 6.666666666666667, 5, 4, 3.3333333333333335, 2.857142857142857, 2.5, + 2.2222222222222223, 2, 1.8181818181818181 + ], + "mode": "lines+markers", + "name": "Harmonic", + "line": { "color": "#dbdb8d", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 }, - "yaxis": { - "type": "linear", - "autorange": false, - "tickmode": "array", - "range": [-5, 105], - "ticktext": ["0", "25", "50", "75", "100"], - "tickvals": [0, 25, 50, 75, 100], - "ticks": "", - "tickcolor": null, - "ticklen": 3.653, - "tickwidth": 0, - "showticklabels": true, - "tickfont": { - "color": "rgba(77,77,77,1)", - "family": "Roboto", - "size": 12.7522 - }, - "tickangle": -0, - "showline": false, - "linecolor": null, - "linewidth": 0, - "showgrid": true, - "domain": [0, 1], - "gridcolor": "rgba(216,216,216,1)", - "gridwidth": 0.6642, - "zeroline": false, - "anchor": "x", - "title": { - "text": "% of students", - "font": { - "color": "rgba(34,34,34,1)", - "family": "Roboto", - "size": 15.9402 - } - }, - "hoverformat": ".2f" + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [2, 3, 6, 11, 18, 27, 38, 51, 66, 83], + "mode": "lines+markers", + "name": "Polynomial 1", + "line": { "color": "#9edae5", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 + }, + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [0, 0, 6, 24, 60, 120, 210, 336, 504, 720], + "mode": "lines+markers", + "name": "Polynomial 2", + "line": { "color": "#393b79", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 + }, + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [0, 1, 16, 81, 256, 625, 1296, 2401, 4096, 6561], + "mode": "lines+markers", + "name": "Polynomial 3", + "line": { "color": "#637939", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 + }, + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 1, 1.6666666666666667, 2.5, 3.4, 4.333333333333333, 5.285714285714286, 6.25, + 7.222222222222222, 8.2, 9.181818181818182 + ], + "mode": "lines+markers", + "name": "Rational", + "line": { "color": "#8c6d31", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 + }, + { + "x": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + "y": [ + 14.279578078204434, 13.808496892213324, 10.564834427515008, + 6.8972988409819305, 5.520939944916718, 7.181768512202332, + 10.098046421574775, 11.660787498142922, 10.814763856863712, + 8.935261038258345 + ], + "mode": "lines+markers", + "name": "Trigonometric", + "line": { "color": "#843c39", "width": 2 }, + "marker": { "size": 4, "opacity": 0.7 }, + "opacity": 0.8 + } + ], + "layout": { + "title": { + "text": "30 Mathematical Functions with Vertical Legend", + "font": { "size": 18, "color": "#333" } }, - "shapes": [ - { - "type": "rect", - "fillcolor": "transparent", - "line": { - "color": "rgba(0,0,0,1)", - "width": 0, - "linetype": "solid" - }, - "yref": "paper", - "xref": "paper", - "x0": 0, - "x1": 1, - "y0": 0, - "y1": 1 - } - ], - "showlegend": true, + "xaxis": { "title": "X Values", "gridcolor": "#e1e5e9", "zeroline": false }, + "yaxis": { "title": "Y Values", "gridcolor": "#e1e5e9", "zeroline": false }, "legend": { "orientation": "v", "x": 0.5, @@ -350,9 +341,12 @@ "itemsizing": "constant", "itemwidth": 30, "itemclick": "toggle", - "itemdoubleclick": "toggleothers", - "maxheight": 0.3 + "itemdoubleclick": "toggleothers" }, - "hovermode": "closest" + "plot_bgcolor": "white", + "paper_bgcolor": "white", + "margin": { "l": 60, "r": 40, "t": 80, "b": 150 }, + "hovermode": "closest", + "showlegend": true } } From e4d4672373677e33d34bd76f6fc3cd20c2658efb Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 22 Jul 2025 10:48:31 -0600 Subject: [PATCH 05/14] Add image of mock --- .../baselines/zz-legend-vertical-maxheight.png | Bin 0 -> 41549 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/image/baselines/zz-legend-vertical-maxheight.png diff --git a/test/image/baselines/zz-legend-vertical-maxheight.png b/test/image/baselines/zz-legend-vertical-maxheight.png new file mode 100644 index 0000000000000000000000000000000000000000..992458e26412db73c49653fed21dee3b75cefe6a GIT binary patch literal 41549 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 literal 0 HcmV?d00001 From 680fa72bd5482bf930c71af842710d8e9739dce8 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 22 Jul 2025 15:17:00 -0600 Subject: [PATCH 06/14] Update ECMAScript version in Falafel call to allow logical OR operator --- tasks/find_locale_strings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/find_locale_strings.js b/tasks/find_locale_strings.js index a898dfe52f8..7c5d3c310cf 100644 --- a/tasks/find_locale_strings.js +++ b/tasks/find_locale_strings.js @@ -28,7 +28,7 @@ function findLocaleStrings() { var code = fs.readFileSync(file, 'utf-8'); var filePartialPath = file.substr(constants.pathToSrc.length); - falafel(code, {locations: true}, function(node) { + falafel(code, { ecmaVersion: 12, locations: true }, function(node) { if(node.type === 'CallExpression' && (node.callee.name === '_' || node.callee.source() === 'Lib._') ) { From 416c6bf75068e302bd7ffadec60acdc717190001 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 22 Jul 2025 15:34:01 -0600 Subject: [PATCH 07/14] Update schema --- test/plot-schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/plot-schema.json b/test/plot-schema.json index 5e1c74f3793..0769e6c18d0 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -3445,7 +3445,7 @@ "valType": "number" }, "maxheight": { - "description": "Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than one. Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px. For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar. The reference height is the full layout height except for vertically oriented legends with a `yref` of `\"paper\"`, where the reference height is the plot height.", + "description": "Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than one. Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px. For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar. The reference height is the full layout height with the following exception: vertically oriented legends with a `yref` of `\"paper\"`, unless the legend is located above/below the plot. In this case, the reference height is the plot height.", "editType": "legend", "min": 0, "valType": "number" @@ -3646,7 +3646,7 @@ "valType": "number" }, "yanchor": { - "description": "Sets the legend's vertical position anchor This anchor binds the `y` position to the *top*, *middle* or *bottom* of the legend. Value *auto* anchors legends at their bottom for `y` values less than or equal to 1/3, anchors legends to at their top for `y` values greater than or equal to 2/3 and anchors legends with respect to their middle otherwise.", + "description": "Sets the legend's vertical position anchor. This anchor binds the `y` position to the *top*, *middle* or *bottom* of the legend. Value *auto* anchors legends at their bottom for `y` values less than or equal to 1/3, anchors legends to at their top for `y` values greater than or equal to 2/3 and anchors legends with respect to their middle otherwise.", "editType": "legend", "valType": "enumerated", "values": [ From d488d8690d1f7856228b117f07027b945412e7ad Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 22 Jul 2025 15:39:40 -0600 Subject: [PATCH 08/14] Fix invalid config in mock --- test/image/mocks/zz-legend-vertical-maxheight.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/image/mocks/zz-legend-vertical-maxheight.json b/test/image/mocks/zz-legend-vertical-maxheight.json index 48acb700774..cc7a487033f 100644 --- a/test/image/mocks/zz-legend-vertical-maxheight.json +++ b/test/image/mocks/zz-legend-vertical-maxheight.json @@ -326,8 +326,8 @@ "text": "30 Mathematical Functions with Vertical Legend", "font": { "size": 18, "color": "#333" } }, - "xaxis": { "title": "X Values", "gridcolor": "#e1e5e9", "zeroline": false }, - "yaxis": { "title": "Y Values", "gridcolor": "#e1e5e9", "zeroline": false }, + "xaxis": { "gridcolor": "#e1e5e9", "zeroline": false }, + "yaxis": { "gridcolor": "#e1e5e9", "zeroline": false }, "legend": { "orientation": "v", "x": 0.5, From 8a322bb5bfbbbd6e8681c8db10d8322373de5289 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 22 Jul 2025 15:43:47 -0600 Subject: [PATCH 09/14] Update more Falafel calls --- tasks/test_syntax.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/test_syntax.js b/tasks/test_syntax.js index 0a5b0eb8cf3..46ddedd43d5 100644 --- a/tasks/test_syntax.js +++ b/tasks/test_syntax.js @@ -42,7 +42,7 @@ function assertJasmineSuites() { var code = fs.readFileSync(file, 'utf-8'); var bn = path.basename(file); - falafel(code, {locations: true}, function(node) { + falafel(code, { ecmaVersion: 12, locations: true }, function(node) { var lineInfo = '[line ' + node.loc.start.line + '] :'; if(node.type === 'Identifier' && BLACK_LIST.indexOf(node.name) !== -1) { @@ -108,7 +108,7 @@ function assertSrcContents() { // parse through code string while keeping track of comments var comments = []; - falafel(code, {onComment: comments, locations: true}, function(node) { + falafel(code, { ecmaVersion: 12, locations: true, onComment: comments }, function(node) { // look for .classList if(node.type === 'MemberExpression') { var source = node.source(); From 8b24220f89c13ccd019a62259f812737293b32fe Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 5 Aug 2025 14:10:44 -0600 Subject: [PATCH 10/14] Clarify documentation, conditional logic Co-authored-by: Emily KL <4672118+emilykl@users.noreply.github.com> --- src/components/legend/attributes.js | 4 ++-- src/components/legend/draw.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index 5723ba1049b..07640a28dcb 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -38,11 +38,11 @@ module.exports = { min: 0, editType: 'legend', description: [ - 'Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than one.', + 'Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than or equal to 1.', 'Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px.', 'For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar.', 'The reference height is the full layout height with the following exception: vertically oriented legends with', - 'a `yref` of `"paper"`, unless the legend is located above/below the plot. In this case, the reference height', + 'a `yref` of `"paper" located to the side of the plot. In this case, the reference height', 'is the plot height.' ].join(' ') }, diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index e250ad55a22..65ee8f436ef 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -771,7 +771,7 @@ function computeLegendDimensions(gd, groups, traces, legendObj) { const { orientation, yref } = legendObj; let { maxheight } = legendObj; - const useFullLayoutHeight = isBelowPlotArea || isAbovePlotArea && !(orientation === "v" && yref === "paper") + const useFullLayoutHeight = (isBelowPlotArea || isAbovePlotArea) && !(orientation === "v" && yref === "paper") // Set default maxheight here since it depends on values passed in by user maxheight ||= useFullLayoutHeight ? 0.5 : 1; const heightToBeScaled = useFullLayoutHeight ? fullLayout.height : gs.h; From 5d04122ce36bbdb78b4fe0d5da928441c06b9583 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Thu, 7 Aug 2025 10:23:11 -0600 Subject: [PATCH 11/14] Fix conditional logic in legend height calculation --- src/components/legend/attributes.js | 5 ++--- src/components/legend/draw.js | 2 +- test/plot-schema.json | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index 07640a28dcb..eb343757494 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -41,9 +41,8 @@ module.exports = { 'Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than or equal to 1.', 'Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px.', 'For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar.', - 'The reference height is the full layout height with the following exception: vertically oriented legends with', - 'a `yref` of `"paper" located to the side of the plot. In this case, the reference height', - 'is the plot height.' + 'The reference height is the full layout height with the following exceptions: legends to the side of the plot', + 'or vertically oriented legends with a `yref` of `"paper". In this case, the reference height is the plot height.' ].join(' ') }, borderwidth: { diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 65ee8f436ef..da4371ff12a 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -771,7 +771,7 @@ function computeLegendDimensions(gd, groups, traces, legendObj) { const { orientation, yref } = legendObj; let { maxheight } = legendObj; - const useFullLayoutHeight = (isBelowPlotArea || isAbovePlotArea) && !(orientation === "v" && yref === "paper") + const useFullLayoutHeight = isBelowPlotArea || isAbovePlotArea || !(orientation === "v" && yref === "paper") // Set default maxheight here since it depends on values passed in by user maxheight ||= useFullLayoutHeight ? 0.5 : 1; const heightToBeScaled = useFullLayoutHeight ? fullLayout.height : gs.h; diff --git a/test/plot-schema.json b/test/plot-schema.json index a3b8626f14c..de66ec6e602 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -3451,7 +3451,7 @@ "valType": "number" }, "maxheight": { - "description": "Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than one. Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px. For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar. The reference height is the full layout height with the following exception: vertically oriented legends with a `yref` of `\"paper\"`, unless the legend is located above/below the plot. In this case, the reference height is the plot height.", + "description": "Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than or equal to 1. Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px. For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar. The reference height is the full layout height with the following exceptions: legends to the side of the plot or vertically oriented legends with a `yref` of `\"paper\". In this case, the reference height is the plot height.", "editType": "legend", "min": 0, "valType": "number" From dbe140dcc613805522348b46f9dbf4ea1b145751 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Thu, 7 Aug 2025 13:58:48 -0600 Subject: [PATCH 12/14] Switch maxheight description back. --- src/components/legend/attributes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index eb343757494..0d7f0d49e9f 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -41,8 +41,8 @@ module.exports = { 'Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than or equal to 1.', 'Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px.', 'For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar.', - 'The reference height is the full layout height with the following exceptions: legends to the side of the plot', - 'or vertically oriented legends with a `yref` of `"paper". In this case, the reference height is the plot height.' + 'The reference height is the full layout height with the following exception: vertically oriented legends with', + 'a `yref` of `"paper", located to the side of the plot. In this case, the reference height is the plot height.' ].join(' ') }, borderwidth: { From 95756cc668e22a6764f08259a1fe86d5f4206703 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Thu, 7 Aug 2025 14:06:26 -0600 Subject: [PATCH 13/14] Simplify boolean logic even more Co-authored-by: Emily KL <4672118+emilykl@users.noreply.github.com> --- src/components/legend/draw.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index da4371ff12a..8c7907358a1 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -771,7 +771,7 @@ function computeLegendDimensions(gd, groups, traces, legendObj) { const { orientation, yref } = legendObj; let { maxheight } = legendObj; - const useFullLayoutHeight = isBelowPlotArea || isAbovePlotArea || !(orientation === "v" && yref === "paper") + const useFullLayoutHeight = isBelowPlotArea || isAbovePlotArea || orientation !== "v" || yref !== "paper" // Set default maxheight here since it depends on values passed in by user maxheight ||= useFullLayoutHeight ? 0.5 : 1; const heightToBeScaled = useFullLayoutHeight ? fullLayout.height : gs.h; From 82499ad8cb314d4e55ff0e714909615416b61cf4 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Thu, 7 Aug 2025 14:11:26 -0600 Subject: [PATCH 14/14] Update schema --- test/plot-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plot-schema.json b/test/plot-schema.json index de66ec6e602..49689537d4e 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -3451,7 +3451,7 @@ "valType": "number" }, "maxheight": { - "description": "Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than or equal to 1. Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px. For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar. The reference height is the full layout height with the following exceptions: legends to the side of the plot or vertically oriented legends with a `yref` of `\"paper\". In this case, the reference height is the plot height.", + "description": "Sets the max height (in px) of the legend, or max height ratio (reference height * ratio) if less than or equal to 1. Default value is: 0.5 for horizontal legends; 1 for vertical legends. The minimum allowed height is 30px. For a ratio of 0.5, the legend will take up to 50% of the reference height before displaying a scrollbar. The reference height is the full layout height with the following exception: vertically oriented legends with a `yref` of `\"paper\", located to the side of the plot. In this case, the reference height is the plot height.", "editType": "legend", "min": 0, "valType": "number"