Skip to content

Legend position and offset#7564

Merged
PavelVanecek merged 5 commits into
mainfrom
legend-position
Jul 20, 2026
Merged

Legend position and offset#7564
PavelVanecek merged 5 commits into
mainfrom
legend-position

Conversation

@PavelVanecek

@PavelVanecek PavelVanecek commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Description

Instead of old align + verticalAlign props, Legend now has the same position and offset props as Label does. And applies different defaults based on the position too. This should make it more convenient and intuitive to customize the Legend.

Motivation and Context

Easier to use API

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • I have added a storybook story or VR test, or extended an existing story or VR test to show my changes

Summary by CodeRabbit

  • New Features

    • Added flexible Legend positioning with position (including inside placements) and coordinate-based placement.
    • Added configurable Legend offset, with improved default layout auto behavior.
    • Re-exported CartesianPosition for easier typing.
    • Added an interactive Legend positioning example with controls.
  • Bug Fixes

    • Improved reserved chart space for outside-positioned legends.
    • Improved tooltip translation when tooltip dimensions aren’t available.
    • Improved legend label/item styling for more reliable wrapping and whitespace.
  • Documentation / Tests

    • Expanded visual and automated test coverage for Legend.position (including long-text scenarios).

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 92278cff-9046-407b-9511-ee88d524133f

📥 Commits

Reviewing files that changed from the base of the PR and between af45514 and 2d5e3b9.

⛔ Files ignored due to path filters (3)
  • test-vr/__snapshots__/tests/www/IndexView.spec-vr.tsx-snapshots/Index-Line-Chart-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/IndexView.spec-vr.tsx-snapshots/Index-Line-Chart-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/IndexView.spec-vr.tsx-snapshots/Index-Line-Chart-1-webkit-linux.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • src/util/ChartUtils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/util/ChartUtils.ts

Walkthrough

Legend now supports Cartesian position and numeric offset placement, with new positioning types, selectors, rendering logic, tests, documentation examples, and package snapshot entries.

Changes

Legend Cartesian positioning

Layer / File(s) Summary
Positioning contracts and state
src/cartesian/*, src/state/*, src/index.ts, scripts/snapshots/*
Separates input and calculated position types, adds positioning helpers and selectors, extends legend state with position and offset, and updates exports and snapshots.
Legend layout and offset flow
src/component/Legend.tsx, src/util/ChartUtils.ts, src/component/DefaultLegendContent.tsx
Derives layout from position, selects view boxes, applies translation and offsets, dispatches new settings, and updates legend item wrapping styles.
Label and tooltip contracts
src/component/Label.tsx, src/context/chartLayoutContext.tsx, src/component/TooltipBoundingBox.tsx, src/util/tooltip/translate.ts
Moves view-box conversion to the Cartesian module and adds nullable tooltip bounding-box handling.
Validation and regression coverage
test/cartesian/*, test/component/Legend/*, test/state/*, test/util/ChartUtils/*, test-vr/tests/*
Covers helper output, position types, CSS placement, state defaults, chart offsets, visual rendering, long labels, and compatibility updates.
Documentation and examples
www/src/docs/exampleComponents/Legend/*, www/src/views/IndexView/IndexLineChart.tsx, omnidoc/*
Adds interactive Legend positioning examples, registers the examples, updates the index chart, and adjusts documentation verification rules.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LegendImpl
  participant ReduxSelectors
  participant getCartesianPosition
  participant ChartOffset
  participant LegendWrapper
  LegendImpl->>ReduxSelectors: read legend area and chart view box
  LegendImpl->>getCartesianPosition: calculate position and anchors
  LegendImpl->>ChartOffset: reserve space for outside placement
  LegendImpl->>LegendWrapper: apply CSS translation and offset styles
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: ckifer

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding Legend position and offset support.
Description check ✅ Passed The description covers the feature, motivation, change type, and checklist, but omits the issue link and detailed testing notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch legend-position

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 34.2kB (0.61%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
recharts/bundle-cjs 1.46MB 10.42kB (0.72%) ⬆️
recharts/bundle-es6 1.28MB 9.41kB (0.74%) ⬆️
recharts/bundle-umd 591.34kB 4.52kB (0.77%) ⬆️
recharts/bundle-treeshaking-cartesian 729.91kB 2.88kB (0.4%) ⬆️
recharts/bundle-treeshaking-polar 479.8kB 1.96kB (0.41%) ⬆️
recharts/bundle-treeshaking-treemap 393.24kB 1.67kB (0.43%) ⬆️
recharts/bundle-treeshaking-sunburst 351.32kB 1.67kB (0.48%) ⬆️
recharts/bundle-treeshaking-sankey 384.81kB 1.67kB (0.44%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: recharts/bundle-cjs

Assets Changed:

Asset Name Size Change Total Size Change (%)
cartesian/CartesianAxis.js 986 bytes 22.11kB 4.67%
util/ChartUtils.js 1.42kB 21.87kB 6.93% ⚠️
component/Label.js 106 bytes 17.59kB 0.61%
component/Legend.js 4.33kB 15.79kB 37.83% ⚠️
context/chartLayoutContext.js -382 bytes 10.06kB -3.66%
cartesian/getCartesianPosition.js 601 bytes 8.86kB 7.27% ⚠️
component/DefaultLegendContent.js 422 bytes 8.28kB 5.37% ⚠️
component/TooltipBoundingBox.js -77 bytes 8.09kB -0.94%
state/selectors/selectChartOffsetInternal.js 224 bytes 5.31kB 4.4%
util/tooltip/translate.js 14 bytes 4.32kB 0.33%
state/legendSlice.js 150 bytes 3.01kB 5.25% ⚠️
cartesian/cartesianPositionToCSSTranslate.js (New) 1.5kB 1.5kB 100.0% 🚀
state/selectors/selectLegendArea.js (New) 670 bytes 670 bytes 100.0% 🚀
cartesian/cartesianViewBoxToTrapezoid.js (New) 453 bytes 453 bytes 100.0% 🚀
view changes for bundle: recharts/bundle-treeshaking-polar

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 1.96kB 479.8kB 0.41%
view changes for bundle: recharts/bundle-treeshaking-sunburst

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 1.67kB 351.32kB 0.48%
view changes for bundle: recharts/bundle-es6

Assets Changed:

Asset Name Size Change Total Size Change (%)
cartesian/CartesianAxis.js 920 bytes 20.72kB 4.65%
util/ChartUtils.js 1.32kB 19.76kB 7.18% ⚠️
component/Label.js 59 bytes 15.81kB 0.37%
component/Legend.js 4.17kB 14.5kB 40.44% ⚠️
context/chartLayoutContext.js -314 bytes 8.79kB -3.45%
cartesian/getCartesianPosition.js 544 bytes 8.56kB 6.79% ⚠️
component/DefaultLegendContent.js 422 bytes 7.31kB 6.12% ⚠️
component/TooltipBoundingBox.js -77 bytes 7.25kB -1.05%
state/selectors/selectChartOffsetInternal.js 28 bytes 4.59kB 0.61%
index.js -1 bytes 4.57kB -0.02%
util/tooltip/translate.js 14 bytes 3.9kB 0.36%
state/legendSlice.js 150 bytes 2.61kB 6.1% ⚠️
cartesian/cartesianPositionToCSSTranslate.js (New) 1.35kB 1.35kB 100.0% 🚀
state/selectors/selectLegendArea.js (New) 497 bytes 497 bytes 100.0% 🚀
cartesian/cartesianViewBoxToTrapezoid.js (New) 313 bytes 313 bytes 100.0% 🚀
view changes for bundle: recharts/bundle-treeshaking-cartesian

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 2.88kB 729.91kB 0.4%
view changes for bundle: recharts/bundle-umd

Assets Changed:

Asset Name Size Change Total Size Change (%)
Recharts.js 4.52kB 591.34kB 0.77%
view changes for bundle: recharts/bundle-treeshaking-treemap

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 1.67kB 393.24kB 0.43%
view changes for bundle: recharts/bundle-treeshaking-sankey

Assets Changed:

Asset Name Size Change Total Size Change (%)
bundle.js 1.67kB 384.81kB 0.44%

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (8)
src/component/TooltipBoundingBox.tsx (1)

97-117: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use CSSProperties consistently.

Since CSSProperties was imported from react and used in the props definition, consider updating positionStyle and outerStyle to use it instead of React.CSSProperties for consistency.

♻️ Proposed fix
-  const positionStyle: React.CSSProperties = props.hasPortalFromProps
+  const positionStyle: CSSProperties = props.hasPortalFromProps
     ? {}
     : {
         transition: resolveTransitionProperty({
           prefersReducedMotion,
           isAnimationActive: props.isAnimationActive,
           active: props.active,
           animationDuration: props.animationDuration,
           animationEasing: props.animationEasing,
         }),
         ...cssProperties,
         pointerEvents: 'none',
         position: 'absolute',
         top: 0,
         left: 0,
       };
-  const outerStyle: React.CSSProperties = {
+  const outerStyle: CSSProperties = {
     ...positionStyle,
     visibility: !state.dismissed && props.active && props.hasPayload ? 'visible' : 'hidden',
     ...props.wrapperStyle,
   };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/component/TooltipBoundingBox.tsx` around lines 97 - 117, Update the
positionStyle and outerStyle declarations in TooltipBoundingBox to use the
imported CSSProperties type directly instead of React.CSSProperties, matching
the existing props typing convention.
www/src/views/IndexView/IndexLineChart.tsx (1)

74-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use CSS variables to support theme changes.

The surrounding LineChart heavily uses CSS variables (e.g., var(--color-border-3) and var(--color-surface-base)) to seamlessly support theming, such as dark mode toggles on the landing page. Hardcoding black and white in the wrapperStyle will cause visual inconsistencies when the theme changes. Consider updating the border and background properties to utilize these existing CSS variables.

🎨 Proposed fix
       <Legend
         position="insideTopRight"
         offset={20}
-        wrapperStyle={{ border: '1px solid black', borderRadius: 5, padding: '1ex', background: 'white' }}
+        wrapperStyle={{ border: '1px solid var(--color-border-3)', borderRadius: 5, padding: '1ex', background: 'var(--color-surface-base)' }}
       />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@www/src/views/IndexView/IndexLineChart.tsx` around lines 74 - 78, Update the
Legend wrapperStyle in IndexLineChart to replace hardcoded black and white
border/background values with the existing theme CSS variables used by the
surrounding LineChart, preserving the current border width, radius, padding, and
positioning.
src/component/Legend.tsx (2)

299-308: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Avoid unconditionally calling getCartesianPosition.

getCartesianPosition is called on every render, even when props.position is undefined (which uses the legacy positioning flow). Consider calling it conditionally to avoid unnecessary computation and to guard against unexpected behavior when the position isn't provided.

♻️ Proposed refactor
-  const positionResult = getCartesianPosition({
-    /*
-     * When calculating the position we use two different view boxes.
-     * Inside positions use the plot area; outside positions use the margin-inset
-     * chart area, placing the Legend beyond any axes.
-     */
-    viewBox: positionViewBox ?? { x: 0, y: 0, width: chartWidth, height: chartHeight },
-    position: props.position,
-    offset: props.offset ?? 0,
-  });
+  const positionResult = props.position
+    ? getCartesianPosition({
+        /*
+         * When calculating the position we use two different view boxes.
+         * Inside positions use the plot area; outside positions use the margin-inset
+         * chart area, placing the Legend beyond any axes.
+         */
+        viewBox: positionViewBox ?? { x: 0, y: 0, width: chartWidth, height: chartHeight },
+        position: props.position,
+        offset: props.offset ?? 0,
+      })
+    : null;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/component/Legend.tsx` around lines 299 - 308, Update the position
calculation around getCartesianPosition so it is invoked only when
props.position is defined; preserve the legacy positioning flow when no position
is provided and avoid passing an undefined position to the helper.

311-318: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Safe access to positionResult fields.

If you apply the suggested refactor to make positionResult conditional, ensure that you also update the positionStyle block to safely access its fields.

♻️ Proposed refactor
-  const positionStyle: React.CSSProperties | undefined = props.position
+  const positionStyle: React.CSSProperties | undefined = props.position && positionResult
     ? {
         width: 'fit-content',
         height: 'fit-content',
         top: positionResult.y + (outsidePositionOffset.top ?? 0),
         left: positionResult.x + (outsidePositionOffset.left ?? 0),
         transform: cartesianPositionToCSSTranslate(positionResult.horizontalAnchor, positionResult.verticalAnchor),
       }
     : getDefaultPosition(wrapperStyle, props, margin, chartWidth, chartHeight, lastBoundingBox);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/component/Legend.tsx` around lines 311 - 318, Update the positionStyle
construction in Legend so it only reads positionResult.y, positionResult.x,
horizontalAnchor, and verticalAnchor when positionResult exists; preserve the
current offset and transform behavior for the available result.
src/cartesian/getCartesianPosition.tsx (1)

291-300: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Avoid array allocation on every call.

Since this helper may be called frequently during rendering and layout phases, consider avoiding the array allocation on each call by either extracting the array to a module-level constant or using direct string comparisons.

♻️ Proposed refactor (direct comparisons)
 export function isOutsidePosition(position: CartesianPosition | undefined): boolean {
   if (position == null) {
     return false;
   }
   if (typeof position === 'object') {
     return true;
   }
-  const absolutePositions = ['top', 'left', 'right', 'bottom'];
-  return absolutePositions.includes(position);
+  return position === 'top' || position === 'left' || position === 'right' || position === 'bottom';
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cartesian/getCartesianPosition.tsx` around lines 291 - 300, Update
isOutsidePosition to avoid creating the absolutePositions array on every
invocation; use direct comparisons or a module-level constant for the allowed
position values while preserving the current null, object, and string behavior.
test-vr/tests/LegendPositionExample.spec-vr.tsx (1)

3-3: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the .tsx extension from the import path.

While Playwright's bundler might resolve this correctly, explicitly including .tsx in import paths is an anti-pattern in standard TypeScript module resolution. Consider removing it for consistency with typical TS import practices.

💡 Proposed change
-import LegendPositionExample from '../../www/src/docs/exampleComponents/Legend/LegendPositionExample.tsx';
+import LegendPositionExample from '../../www/src/docs/exampleComponents/Legend/LegendPositionExample';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test-vr/tests/LegendPositionExample.spec-vr.tsx` at line 3, Update the
LegendPositionExample import in LegendPositionExample.spec-vr.tsx to omit the
explicit .tsx extension, preserving the same module target and import behavior.
test/component/Legend/Legend.spec.tsx (2)

1875-1875: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out code or add a TODO.
These assertions are commented out, likely because they break across React versions (as noted in adjacent comments). Dead code should ideally be removed or accompanied by a TODO: comment so it can be actively tracked for future resolution.

  • test/component/Legend/Legend.spec.tsx#L1875-L1875: remove the commented-out assertion or add a TODO for the offset spy call count.
  • test/component/Legend/Legend.spec.tsx#L1904-L1904: remove the commented-out assertion or add a TODO.
  • test/component/Legend/Legend.spec.tsx#L1933-L1933: remove the commented-out assertion or add a TODO.
  • test/component/Legend/Legend.spec.tsx#L1962-L1962: remove the commented-out assertion or add a TODO.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/component/Legend/Legend.spec.tsx` at line 1875, Remove the commented-out
offset spy assertions at test/component/Legend/Legend.spec.tsx lines 1875, 1904,
1933, and 1962, or add a clear TODO comment at each location explaining the
React-version compatibility issue and future resolution needed.

538-556: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clean up conversational scratchpad comments.
These comments read like draft notes or manual calculation traces (e.g., "If input y is 0 (from chart dimensions?), wait."). Consider removing them or condensing them into brief explanations of why the styles are expected.

  • test/component/Legend/Legend.spec.tsx#L538-L556: condense or remove the manual calculation trace for position="top".
  • test/component/Legend/Legend.spec.tsx#L594-L599: condense or remove the manual calculation trace for position="insideBottomRight".
  • test/component/Legend/Legend.spec.tsx#L635-L638: condense or remove the manual calculation trace for position="left".
  • test/component/Legend/Legend.spec.tsx#L696-L700: condense or remove the trace for coordinate object positioning.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/component/Legend/Legend.spec.tsx` around lines 538 - 556, Clean up the
scratchpad-style calculation comments in test/component/Legend/Legend.spec.tsx
at lines 538-556, 594-599, 635-638, and 696-700. Remove conversational notes and
manual traces, or condense each section to a brief explanation of the expected
positioning styles.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/cartesian/getCartesianPosition.tsx`:
- Around line 291-300: Update isOutsidePosition to avoid creating the
absolutePositions array on every invocation; use direct comparisons or a
module-level constant for the allowed position values while preserving the
current null, object, and string behavior.

In `@src/component/Legend.tsx`:
- Around line 299-308: Update the position calculation around
getCartesianPosition so it is invoked only when props.position is defined;
preserve the legacy positioning flow when no position is provided and avoid
passing an undefined position to the helper.
- Around line 311-318: Update the positionStyle construction in Legend so it
only reads positionResult.y, positionResult.x, horizontalAnchor, and
verticalAnchor when positionResult exists; preserve the current offset and
transform behavior for the available result.

In `@src/component/TooltipBoundingBox.tsx`:
- Around line 97-117: Update the positionStyle and outerStyle declarations in
TooltipBoundingBox to use the imported CSSProperties type directly instead of
React.CSSProperties, matching the existing props typing convention.

In `@test-vr/tests/LegendPositionExample.spec-vr.tsx`:
- Line 3: Update the LegendPositionExample import in
LegendPositionExample.spec-vr.tsx to omit the explicit .tsx extension,
preserving the same module target and import behavior.

In `@test/component/Legend/Legend.spec.tsx`:
- Line 1875: Remove the commented-out offset spy assertions at
test/component/Legend/Legend.spec.tsx lines 1875, 1904, 1933, and 1962, or add a
clear TODO comment at each location explaining the React-version compatibility
issue and future resolution needed.
- Around line 538-556: Clean up the scratchpad-style calculation comments in
test/component/Legend/Legend.spec.tsx at lines 538-556, 594-599, 635-638, and
696-700. Remove conversational notes and manual traces, or condense each section
to a brief explanation of the expected positioning styles.

In `@www/src/views/IndexView/IndexLineChart.tsx`:
- Around line 74-78: Update the Legend wrapperStyle in IndexLineChart to replace
hardcoded black and white border/background values with the existing theme CSS
variables used by the surrounding LineChart, preserving the current border
width, radius, padding, and positioning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 47dfad44-eb08-4704-8083-41b0aaa71616

📥 Commits

Reviewing files that changed from the base of the PR and between ca62d1c and 9b8de11.

⛔ Files ignored due to path filters (53)
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-align-without-offset-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-align-without-offset-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-align-without-offset-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-custom-offset-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-custom-offset-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-custom-offset-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-default-offset-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-default-offset-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-default-offset-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---bottom-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---bottom-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---bottom-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---center-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---center-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---center-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottom-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottom-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottom-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomLeft-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomLeft-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomLeft-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomRight-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomRight-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomRight-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideLeft-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideLeft-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideLeft-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideRight-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideRight-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideRight-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTop-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTop-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTop-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopLeft-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopLeft-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopLeft-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopRight-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopRight-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopRight-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---left-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---left-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---left-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---right-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---right-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---right-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---top-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---top-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---top-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/Customize.spec-vr.tsx-snapshots/CustomizeLegendAndTooltipStyle-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/Customize.spec-vr.tsx-snapshots/CustomizeLegendAndTooltipStyle-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/IndexView.spec-vr.tsx-snapshots/Index-Line-Chart-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/IndexView.spec-vr.tsx-snapshots/Index-Line-Chart-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/www/IndexView.spec-vr.tsx-snapshots/Index-Line-Chart-1-webkit-linux.png is excluded by !**/*.png
📒 Files selected for processing (36)
  • omnidoc/commentSimilarityExceptions.ts
  • scripts/snapshots/es6Files.txt
  • scripts/snapshots/libFiles.txt
  • scripts/snapshots/typesFiles.txt
  • src/cartesian/XAxis.tsx
  • src/cartesian/cartesianPositionToCSSTranslate.tsx
  • src/cartesian/cartesianViewBoxToTrapezoid.tsx
  • src/cartesian/getCartesianPosition.tsx
  • src/component/DefaultLegendContent.tsx
  • src/component/Label.tsx
  • src/component/Legend.tsx
  • src/component/TooltipBoundingBox.tsx
  • src/context/chartLayoutContext.tsx
  • src/index.ts
  • src/state/legendSlice.ts
  • src/state/selectors/legendSelectors.ts
  • src/state/selectors/selectChartOffsetInternal.ts
  • src/state/selectors/selectLegendArea.ts
  • src/util/ChartUtils.ts
  • src/util/tooltip/translate.ts
  • test-vr/tests/LegendPosition.spec-vr.tsx
  • test-vr/tests/LegendPositionComponents.tsx
  • test-vr/tests/LegendPositionExample.spec-vr.tsx
  • test/cartesian/cartesianPositionToCSSTranslate.spec.ts
  • test/cartesian/getCartesianPosition.spec.ts
  • test/component/Legend/Legend.itemSorter.spec.tsx
  • test/component/Legend/Legend.spec.tsx
  • test/component/Legend/Legend.typed.spec.tsx
  • test/component/Tooltip/Tooltip.visibility.spec.tsx
  • test/state/selectors/legendSelectors.spec.tsx
  • test/state/selectors/selectChartOffset.spec.tsx
  • test/util/ChartUtils/appendOffsetOfLegend.spec.tsx
  • www/src/docs/exampleComponents/Legend/LegendPositionExample.tsx
  • www/src/docs/exampleComponents/Legend/index.ts
  • www/src/docs/exampleComponents/Legend/index.tsx
  • www/src/views/IndexView/IndexLineChart.tsx
💤 Files with no reviewable changes (2)
  • www/src/docs/exampleComponents/Legend/index.ts
  • src/context/chartLayoutContext.tsx

@chromatic-com

chromatic-com Bot commented Jul 19, 2026

Copy link
Copy Markdown

Tip

All tests passed and all changes approved!

🟢 UI Tests: 198 tests unchanged
🟢 UI Review: 198 stories published -- no changes
Storybook icon Storybook Publish: 198 stories published

@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.07143% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.21%. Comparing base (14c1a2d) to head (2d5e3b9).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...exampleComponents/Legend/LegendPositionExample.tsx 60.00% 3 Missing and 3 partials ⚠️
src/cartesian/cartesianViewBoxToTrapezoid.tsx 83.33% 1 Missing ⚠️
src/cartesian/getCartesianPosition.tsx 91.66% 1 Missing ⚠️
src/component/Legend.tsx 97.43% 1 Missing ⚠️
src/util/ChartUtils.ts 91.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7564      +/-   ##
==========================================
+ Coverage   88.20%   88.21%   +0.01%     
==========================================
  Files         615      619       +4     
  Lines       14272    14369      +97     
  Branches     3588     3638      +50     
==========================================
+ Hits        12588    12676      +88     
- Misses       1494     1499       +5     
- Partials      190      194       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/util/ChartUtils.ts (1)

101-102: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clarify the comment regarding position-based legends moving the plot area.

The comment on line 101 states that position-based legends "must not move the plot area", but the very next block (if (isOutsidePosition(position))) modifies the chart's offsets to do exactly that.

Consider updating the comment to clarify that this restriction applies primarily to inside positions, while outside positions still require offset adjustments to make room for the legend.

📝 Proposed comment update
-      // Position-based legends are absolutely placed and must not move the plot area.
+      // Position-based legends are absolutely placed. They only move the plot area if they are positioned outside.
       if (isOutsidePosition(position)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/util/ChartUtils.ts` around lines 101 - 102, Update the comment
immediately above the isOutsidePosition(position) check to clarify that
inside-position legends do not move the plot area, while outside-position
legends adjust chart offsets to reserve space.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/util/ChartUtils.ts`:
- Around line 101-102: Update the comment immediately above the
isOutsidePosition(position) check to clarify that inside-position legends do not
move the plot area, while outside-position legends adjust chart offsets to
reserve space.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a53f513-7bcd-4442-9a82-30a79fb6d25c

📥 Commits

Reviewing files that changed from the base of the PR and between 9b8de11 and af45514.

⛔ Files ignored due to path filters (33)
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-custom-offset-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-custom-offset-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-custom-offset-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-default-offset-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-default-offset-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/Legend-position-with-default-offset-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-bottom-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-bottom-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-bottom-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-insideRight-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-insideRight-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-insideRight-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-left-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-left-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPosition.spec-vr.tsx-snapshots/VeryLongLegendText-with-position-left-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomRight-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomRight-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideBottomRight-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideLeft-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideLeft-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideLeft-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideRight-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideRight-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideRight-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopRight-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopRight-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---insideTopRight-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---left-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---left-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---left-1-webkit-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---right-1-chromium-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---right-1-firefox-linux.png is excluded by !**/*.png
  • test-vr/__snapshots__/tests/LegendPositionExample.spec-vr.tsx-snapshots/LegendPositionExample---right-1-webkit-linux.png is excluded by !**/*.png
📒 Files selected for processing (13)
  • omnidoc/commentSimilarityExceptions.ts
  • omnidoc/verifyExamples.spec.ts
  • src/cartesian/getCartesianPosition.tsx
  • src/component/DefaultLegendContent.tsx
  • src/component/Legend.tsx
  • src/component/TooltipBoundingBox.tsx
  • src/util/ChartUtils.ts
  • test-vr/tests/LegendPosition.spec-vr.tsx
  • test-vr/tests/LegendPositionComponents.tsx
  • test/component/Legend/Legend.spec.tsx
  • test/util/ChartUtils/appendOffsetOfLegend.spec.tsx
  • www/src/docs/exampleComponents/Legend/LegendPositionExample.tsx
  • www/src/views/IndexView/IndexLineChart.tsx
💤 Files with no reviewable changes (1)
  • omnidoc/verifyExamples.spec.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • www/src/views/IndexView/IndexLineChart.tsx
  • src/component/DefaultLegendContent.tsx
  • src/component/TooltipBoundingBox.tsx
  • test/component/Legend/Legend.spec.tsx
  • src/component/Legend.tsx
  • src/cartesian/getCartesianPosition.tsx

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@PavelVanecek
PavelVanecek merged commit 082495a into main Jul 20, 2026
65 checks passed
@PavelVanecek
PavelVanecek deleted the legend-position branch July 20, 2026 08:41
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.

1 participant