Conversation
WalkthroughThis PR adds extensive defensive null/undefined checks across Sankey rendering logic, broadens type signatures in selectors to accept optional parameters, tightens type guard constraints, and hardens event handling to safely tolerate missing or undefined data without throwing errors. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (2 warnings, 1 inconclusive)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/chart/Sankey.tsx (1)
782-801: Potential index mismatch after filtering undefined links.In
AllSankeyLinkElements, you iterate overlinksarray (line 784) and accessmodifiedLinks[i](line 785). However, sincemodifiedLinksis filtered using.filter(isNotNil)(line 998), its indices no longer align with the originallinksarray.For example, if
buildLinkPropsreturnsundefinedfor the link at index 2,modifiedLinkswill have fewer elements thanlinks, causingmodifiedLinks[i]to reference the wrong link or beundefinedfor valid links at higher indices.Consider either:
- Keep
undefinedentries inmodifiedLinkswithout filtering (the null check at line 786-788 already handles this), or- Iterate over
modifiedLinksdirectly instead oflinks:- {links.map((link: SankeyLink, i: number) => { - const linkProps = modifiedLinks[i]; - if (linkProps == null) { - return null; - } + {modifiedLinks.map((linkProps: LinkProps) => { + const link = linkProps.payload; return ( <SankeyLinkElement - key={`link-${link.source}-${link.target}-${link.value}`} + key={`link-${linkProps.index}`} props={linkProps} linkContent={linkContent} - i={i} + i={linkProps.index} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} onClick={onClick} dataKey={dataKey} /> ); })}
🧹 Nitpick comments (1)
src/state/selectors/axisSelectors.ts (1)
58-62: itemAxisPredicate typing/doc now cover more than just cartesian itemsBroadening the predicate parameter to
BaseCartesianGraphicalItemSettings | BasePolarGraphicalItemSettingsmakes sense for reusing this helper across both cartesian and polar items; the runtime checks using'xAxisId' in item,'angleAxisId' in item, etc. are safe.Two small follow‑ups you might consider:
- The JSDoc above still says “Filters CartesianGraphicalItemSettings…”, which is no longer accurate given the new
Base*imports. It would be clearer to update it to “graphical item settings (cartesian or polar)” or similar.selectAxisPredicateis annotated as returning(item: CartesianGraphicalItemSettings) => booleanbut is implemented viaitemAxisPredicate, which returns a predicate over the union of base cartesian + base polar settings. This works today, but the exposed type suggests cartesian‑only. Either widening that callback type to the same union or introducing a shared alias (e.g.AxisRelevantGraphicalItemSettings) would keep the types and implementation aligned and avoid future confusion.Also applies to: 293-317
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/chart/Sankey.tsx(21 hunks)src/state/selectors/axisSelectors.ts(5 hunks)src/state/selectors/tooltipSelectors.ts(7 hunks)src/state/touchEventsMiddleware.ts(2 hunks)src/state/types/StackedGraphicalItem.ts(1 hunks)src/util/getActiveCoordinate.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*.{ts,tsx}: Never useanytype (implicit or explicit) in TypeScript code
Preferunknownoveranyand refine the type in TypeScript
Type function parameters and return values explicitly in TypeScript, do not rely on implicit any or inference; exceptions are React components and trivial functions
Do not useastype assertions in TypeScript; the only exception isas const
Files:
src/state/touchEventsMiddleware.tssrc/state/types/StackedGraphicalItem.tssrc/state/selectors/axisSelectors.tssrc/util/getActiveCoordinate.tssrc/chart/Sankey.tsxsrc/state/selectors/tooltipSelectors.ts
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Ensure code lints by running
npm run lintand follows Airbnb's Style Guide
Files:
src/state/touchEventsMiddleware.tssrc/state/types/StackedGraphicalItem.tssrc/state/selectors/axisSelectors.tssrc/util/getActiveCoordinate.tssrc/chart/Sankey.tsxsrc/state/selectors/tooltipSelectors.ts
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Do not hardcode any strings or formatting choices in library code; users should provide localized strings as desired
Files:
src/state/touchEventsMiddleware.tssrc/state/types/StackedGraphicalItem.tssrc/state/selectors/axisSelectors.tssrc/util/getActiveCoordinate.tssrc/chart/Sankey.tsxsrc/state/selectors/tooltipSelectors.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (DEVELOPING.md)
All imports from
rechartsmust use the public API entry point; imports from internal paths likerecharts/types/*orrecharts/src/*are not allowed
Files:
src/state/touchEventsMiddleware.tssrc/state/types/StackedGraphicalItem.tssrc/state/selectors/axisSelectors.tssrc/util/getActiveCoordinate.tssrc/chart/Sankey.tsxsrc/state/selectors/tooltipSelectors.ts
🧠 Learnings (4)
📚 Learning: 2025-11-23T13:30:10.395Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6669
File: www/src/docs/exampleComponents/ScatterChart/ScatterChartWithLabels.tsx:2-2
Timestamp: 2025-11-23T13:30:10.395Z
Learning: The `TooltipIndex` type from recharts is defined in `src/state/tooltipSlice.ts` but is not currently exported from the public API entry points. It should not be imported from `recharts/types/state/tooltipSlice` as this is an internal implementation path. An ESLint rule is needed to prevent regressions.
Applied to files:
src/state/touchEventsMiddleware.tssrc/state/selectors/tooltipSelectors.ts
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : When testing tooltips on hover, use `vi.runOnlyPendingTimers()` after each `userEvent.hover()` call or use the `showTooltip` helper function from `tooltipTestHelpers.ts` to account for requestAnimationFrame delays
Applied to files:
src/state/touchEventsMiddleware.ts
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Verify the number of selector calls using the spy object from `createSelectorTestCase` to spot unnecessary re-renders and improve performance
Applied to files:
src/state/selectors/tooltipSelectors.ts
📚 Learning: 2025-12-06T03:36:59.377Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-06T03:36:59.377Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : All imports from `recharts` must use the public API entry point; imports from internal paths like `recharts/types/*` or `recharts/src/*` are not allowed
Applied to files:
src/state/selectors/tooltipSelectors.ts
🧬 Code graph analysis (5)
src/state/touchEventsMiddleware.ts (2)
src/state/selectors/selectActivePropsFromChartPointer.ts (1)
selectActivePropsFromChartPointer(14-29)src/util/getChartPointer.ts (1)
getChartPointer(16-34)
src/state/types/StackedGraphicalItem.ts (1)
src/state/graphicalItemsSlice.ts (1)
GraphicalItemSettings(21-41)
src/state/selectors/axisSelectors.ts (4)
src/state/graphicalItemsSlice.ts (2)
BaseCartesianGraphicalItemSettings(43-57)BasePolarGraphicalItemSettings(61-64)src/state/referenceElementsSlice.ts (3)
ReferenceDotSettings(13-17)ReferenceAreaSettings(19-24)ReferenceLineSettings(26-30)src/state/selectors/selectTooltipAxisType.ts (1)
RenderableAxisType(12-12)src/util/types.ts (1)
NumberDomain(708-708)
src/chart/Sankey.tsx (2)
src/util/types.ts (1)
SankeyNode(957-971)src/util/DataUtils.ts (1)
isNotNil(202-204)
src/state/selectors/tooltipSelectors.ts (6)
src/state/graphicalItemsSlice.ts (3)
CartesianGraphicalItemSettings(59-59)PolarGraphicalItemSettings(66-66)GraphicalItemSettings(21-41)src/state/selectors/selectTooltipAxisType.ts (1)
selectTooltipAxisType(14-30)src/state/selectors/axisSelectors.ts (2)
itemAxisPredicate(299-317)selectTooltipAxis(595-599)src/state/selectors/arrayEqualityCheck.ts (1)
emptyArraysAreEqualCheck(7-13)src/state/tooltipSlice.ts (1)
TooltipInteractionState(118-162)src/util/types.ts (2)
DataKey(60-60)Coordinate(88-91)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build, Test, Pack
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (14)
src/chart/Sankey.tsx (4)
40-40: Defensive null check for entry value.The updated
getValuehandles undefined entries safely by returning 0 as a fallback.
355-380: Null-safe sorting comparators.Returning
0when encountering null values preserves the relative order of elements with missing data. This is a reasonable defensive approach for handling potentially sparse data.
52-78: Consistent null guards in weighted sum calculations.The added null checks in
getSumWithWeightedSourceandgetSumWithWeightedTargetprevent crashes when links or nodes are missing, returning the accumulated result safely.
678-684:buildLinkPropsnow returnsundefinedfor invalid node references.The change correctly guards against missing source or target nodes, aligning with the updated return type
LinkProps | undefined.src/state/touchEventsMiddleware.ts (2)
29-39: Defensive null check for touch event.While line 23 already verifies
touchEvent.touches.length !== 0, the explicit null check at lines 30-32 provides additional type safety for TypeScript's strict mode. Using a localtouchvariable also improves readability.
51-54: Combined null guards for item tooltip path.The combined check for
document.elementFromPoint == null || touch == nullcorrectly handles both edge cases before attempting to use the touch coordinates.src/state/types/StackedGraphicalItem.ts (1)
27-31: Broadened type guard to accept baseGraphicalItemSettings.The updated type guard correctly:
- Accepts any
GraphicalItemSettings(not justMaybeStackedGraphicalItem)- Uses
'stackId' in graphicalItemto verify the property exists before checking its value- Narrows to
T & DefinitelyStackedGraphicalItemwhich ensures bothstackIdanddataKeyare non-nullThis is a sound type narrowing pattern that aligns with the broader type flexibility introduced in selectors.
src/state/selectors/tooltipSelectors.ts (5)
52-56: Explicit type imports for graphical item settings.The updated imports provide clearer type definitions for the selectors that work with graphical items.
98-119: Explicit return types for graphical item selectors.The explicit return type annotations for
selectAllUnfilteredGraphicalItemsandselectAllGraphicalItemsSettingsimprove code clarity and ensure type consistency across the selector chain.
324-324: Parameter type acceptsundefinedforrealScaleType.This aligns with the upstream selector
selectTooltipAxisRealScaleTypewhich returnsstring | undefined. The function body at line 342 safely handles the undefined case with a comparison check.
415-432: Undefined-safe tooltip interaction state handling.Both
selectActiveTooltipDataKeyandselectActiveTooltipGraphicalItemIdnow explicitly acceptTooltipInteractionState | undefinedand guard against undefined before accessing properties.
456-471: Consistent undefined handling in tooltip coordinate and active state selectors.
selectActiveTooltipCoordinateuses optional chaining (tooltipInteractionState?.coordinate)selectIsTooltipActiveuses nullish coalescing (?? false) for a clean boolean defaultBoth patterns are idiomatic TypeScript for handling potentially undefined values.
src/util/getActiveCoordinate.ts (1)
113-113: Return type broadening tonumber | undefinedis correct.The change is appropriate since the function uses optional chaining (
unsortedTicks[i]?.index) at lines 157 and 164, which can returnundefined. Callers insrc/state/selectors/selectors.ts(lines 217 and 248) properly handle theundefinedreturn value by passing it togetActiveCartesianCoordinateandgetActivePolarCoordinate, both of which acceptnumber | undefinedas theiractiveIndexparameter.src/state/selectors/axisSelectors.ts (1)
936-948: Undefined‑tolerant reference domains look correctThe added
| undefinedondots/areas/linesand the earlyif (… == null) return undefined;guards incombineDotsDomain,combineAreasDomain, andcombineLinesDomainsafely handle optional inputs without changing behavior for existing callers that pass arrays (empty or not). The downstream logic still correctly returnsundefinedwhen there are no numeric coordinates.No issues from a correctness or typings perspective; this is a solid defensive improvement.
Also applies to: 952-966, 992-1006
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6754 +/- ##
==========================================
- Coverage 93.91% 93.79% -0.13%
==========================================
Files 514 514
Lines 42833 42947 +114
Branches 4991 5020 +29
==========================================
+ Hits 40227 40280 +53
- Misses 2600 2661 +61
Partials 6 6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Staging Deployment Details
These deployments will remain available for 30 days. To update snapshots: Comment |
Bundle ReportChanges will increase total bundle size by 7.5kB (0.28%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: recharts/bundle-es6Assets Changed:
view changes for bundle: recharts/bundle-cjsAssets Changed:
view changes for bundle: recharts/bundle-umdAssets Changed:
|
Description
271 errors -> 165
Related Issue
#6645
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.