Skip to content

Add legend.itemheight to set the height of the legend fill swatch - #7925

Open
MannXo wants to merge 2 commits into
plotly:masterfrom
MannXo:feat/7924-legend-itemheight
Open

Add legend.itemheight to set the height of the legend fill swatch#7925
MannXo wants to merge 2 commits into
plotly:masterfrom
MannXo:feat/7924-legend-itemheight

Conversation

@MannXo

@MannXo MannXo commented Jul 29, 2026

Copy link
Copy Markdown

Closes #7924

Adds layout.legend.itemheight, the vertical counterpart to the existing itemwidth, so a taller legend swatch can reveal more of a trace's fillpattern.

The two parts

The fill swatch height was hardcoded in src/components/legend/style.js:

fill.attr('d', pathStart + 'h' + itemWidth + 'v6h-' + itemWidth + 'z')

That v6 is the 6 pixels mentioned in the issue. Making it configurable on its own is not enough, though: legend row height is computed in draw.js as Math.max(height, 16) + 3, so anything taller than about 13px would have overlapped its neighbours. That is most likely the "my vertical alignment is off" in the original report. This PR changes both:

  • attributes.js / defaults.js: new itemheight, min: 6, dflt: 6, editType: 'legend'. min === dflt mirrors itemwidth exactly (min: 30, dflt: 30).
  • style.js: v6 becomes v<itemheight>.
  • draw.js: row height becomes Math.max(height, 16, legendObj.itemheight + 10) + 3, so items grow to fit the swatch.

At the default the rendering is unchanged. Since 6 + 10 === 16, the new term cannot win at itemheight: 6, and the swatch path is still v6. I kept the literal 16 as an independent floor rather than replacing it with itemheight + 10, because those two being equal at the default is a choice of constant on my part, not something I can show about why 16 was originally picked.

The swatch grows downwards, leaving pathStart alone. That keeps the trace line sitting on the top edge of the fill, which is how a filled area actually renders in the plot, and it matches the mock in the issue. It also means a trace with no fill is completely unaffected, since pathStart is what positions the line.

Unified hover labels need no separate handling: hover.js builds its mockLegend through legendSupplyDefaults, so the new attribute is coerced there too.

Before / after

Three fillpattern traces, default versus legend.itemheight: 24. The /, x and . patterns are hard to tell apart at 6px and legible at 24px.

Before After
image image

Testing

npm run test-jasmine -- legend --nowatch gives 132 passing, of which 8 are new. hover_label is 221 passing. npm run lint, npm run test-syntax and npm run test-mock are all clean. test/plot-schema.json regenerated with npm run schema; dist/ deliberately untouched.

New tests:

Test Purpose
reproduces the historical 6px swatch at the default pins M5,0h30v6h-30z so the default cannot drift
grows the fill swatch to the requested height the feature
grows the swatch downwards, leaving the line on top of the fill pins the growth direction
does not move the swatch of a trace without fill guards unfilled traces
grows each legend row so taller swatches do not overlap the draw.js half
leaves row heights untouched at the default no regression for existing charts
applies to unified hover labels without error the mockLegend path
defaults itemheight to 6 and clamps values below the minimum coercion

I verified the tests actually bind to the change rather than passing regardless. Reverting only the style.js line fails exactly the two swatch-growth tests and nothing else; reverting only the draw.js line fails exactly the row-growth test. The default-behaviour guards pass either way by design, which is what stops the feature from degenerating into "move the swatch".

Three things worth a maintainer's call

  1. Scope. itemheight currently drives only the fill swatch. Bar and marker swatches have their own fixed geometry (styleBars draws M6,6H-6V-6H6Z, a 12x12 square). Should itemheight drive those too, or stay fill-only as the issue requests?
  2. min: 6. Chosen to match today's height and to mirror itemwidth's min === dflt. Happy to lower it if you would rather allow thinner swatches.
  3. Label alignment. Because the swatch grows downwards, the label sits nearer the top of a tall swatch rather than centred on it. legend.valign already offers some control here and the default is untouched, so I left text positioning alone rather than adding a second alignment mechanism. Say the word if you would prefer the label centred on the swatch.

No image mock is included yet. The itemwidth precedent is test/image/mocks/legend_itemwidth_dashline.json, so I am happy to add one, but since baselines are generated on ubuntu-latest I would rather take the baseline from CI than commit a macOS render. Let me know and I will add it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Setting itemheight (analogous to itemwidth) to increase height of fill in legend

1 participant