Fixing event handler types#6970
Conversation
WalkthroughThis PR systematically refines TypeScript type signatures across Recharts chart components, replacing generic types ( Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
187bc32 to
50f18e8
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/cartesian/ReferenceDot.tsx`:
- Line 128: The Props type currently intersects Omit<DotProps, 'cx' | 'cy' |
'clipDot' | 'dangerouslySetInnerHTML'> with ReferenceDotProps, causing
conflicting mouse event handler signatures; update the Omit on DotProps in the
Props declaration to also exclude the mouse event handler keys (onClick,
onMouseDown, onMouseUp, onMouseOver, onMouseOut, onMouseEnter, onMouseMove,
onMouseLeave) so ReferenceDotProps' handler types are used (adjust the Omit list
in the Props type near the ReferenceDot.tsx export).
In `@test/cartesian/ReferenceDot.spec.tsx`:
- Around line 349-440: The tests in the 'events' describe use userEventSetup()
(which calls vi.advanceTimersByTime) and render LineChart that depends on Redux
autoBatchEnhancer/requestAnimationFrame, so enable fake timers for deterministic
behavior: add vi.useFakeTimers() in a beforeEach for the describe('events')
block (and restore real timers in an afterEach with vi.useRealTimers()) so that
userEventSetup, vi.advanceTimersByTime, and any RAF/timer-based batching behave
predictably.
🧹 Nitpick comments (10)
test/cartesian/ReferenceLine/ReferenceLine.spec.tsx (3)
688-718: Inconsistent use ofexpectLastCalledWithhelper.Line 690 correctly uses
expectLastCalledWith(onClick, ...), but lines 694, 696, 700, 702, 706, 710, 714, and 718 fall back toexpect(spy).toHaveBeenLastCalledWith(...). The coding guidelines say to preferexpectLastCalledWithfor better typing and autocompletion.♻️ Suggested fix (representative diff for a few lines)
- expect(onMouseEnter).toHaveBeenLastCalledWith(expect.objectContaining({ type: 'mouseenter' })); + expectLastCalledWith(onMouseEnter, expect.objectContaining({ type: 'mouseenter' })); expect(onMouseOver).toHaveBeenCalledTimes(1); - expect(onMouseOver).toHaveBeenLastCalledWith(expect.objectContaining({ type: 'mouseover' })); + expectLastCalledWith(onMouseOver, expect.objectContaining({ type: 'mouseover' }));Apply the same pattern to
onMouseLeave,onMouseOut,onMouseMove,onTouchStart,onTouchMove, andonTouchEnd.As per coding guidelines,
test/**/*.{test,spec}.{ts,tsx}: "Use theexpectLastCalledWithhelper function instead ofexpect(spy).toHaveBeenLastCalledWith(...)for better typing and autocompletion".
685-686: Misleading variable namedotfor a reference line element.The selector targets
.recharts-reference-line .recharts-reference-line-line, which is a line element, not a dot. Consider renaming tolineorrefLinefor clarity.- const dot = container.querySelector('.recharts-reference-line .recharts-reference-line-line'); - assertNotNull(dot); + const refLine = container.querySelector('.recharts-reference-line .recharts-reference-line-line'); + assertNotNull(refLine);Then update all subsequent references accordingly.
669-671: Strayr={3}prop on ReferenceLine.
ris a radius prop that belongs toReferenceDot, notReferenceLine. This appears to be a copy-paste artifact. While it's harmless (it'll be ignored or spread as an SVG attribute), it's misleading in a test that's meant to validate ReferenceLine event handlers.<ReferenceLine x={1} - r={3} onClick={onClick}omnidoc/verifyExamples.spec.ts (1)
97-101: Naming concern:InternalRadarPropsexported as public API.Exporting a type prefixed with
Internalas part of the public API is confusing for consumers — the name implies it's an internal implementation detail. Consider renaming it to something likeRadarEventHandlerPropsorRadarComposedPropsto better communicate its intended public use, or keep it internal and expose a differently named alias.This is a broader concern rooted in
src/index.ts/src/polar/Radar.tsx, not this file specifically.test/polar/Radar.typed.spec.tsx (1)
5-47: Inconsistent handler signatures across Radar event props — is this intentional?
onMouseEnterandonMouseLeavereceive(data: InternalRadarProps, event)while all other mouse/touch handlers receive only(event). If this asymmetry is intentional (matching the actual runtime behavior), it may be worth a brief comment in the test explaining why, similar to the Treemap test'sonClickcomment. If unintentional, the underlying Radar types may need alignment.test/cartesian/ReferenceArea.spec.tsx (1)
561-638: Good event handler coverage for ReferenceArea.The new
eventsdescribe block comprehensively tests all mouse and touch event handlers. A few observations:
Misleading variable name on Line 603:
dotqueries.recharts-reference-area-rect— consider naming itrectorreferenceAreaRectfor clarity.Inconsistent use of
expectLastCalledWith: Line 608 correctly uses theexpectLastCalledWithhelper, but lines 612, 614, 618–620, and 624 fall back toexpect(spy).toHaveBeenLastCalledWith(...). As per coding guidelines: "Use theexpectLastCalledWithhelper function instead ofexpect(spy).toHaveBeenLastCalledWith(...)for better typing and autocompletion."Missing
vi.useFakeTimers(): The test usesuserEventSetup()(which internally relies onvi.advanceTimersByTime) but doesn't set up fake timers. As per coding guidelines: "Usevi.useFakeTimers()in all tests due to Redux autoBatchEnhancer dependency on timers andrequestAnimationFrame."Suggested improvements
describe('events', () => { + vi.useFakeTimers(); + it('should fire event handlers when provided', async () => { const userEvent = userEventSetup();- const dot = container.querySelector('.recharts-reference-area-rect'); - assertNotNull(dot); + const rect = container.querySelector('.recharts-reference-area-rect'); + assertNotNull(rect); - await userEvent.click(dot); + await userEvent.click(rect); expect(onClick).toHaveBeenCalledTimes(1); expectLastCalledWith(onClick, expect.objectContaining({ type: 'click' })); - await userEvent.hover(dot); + await userEvent.hover(rect); expect(onMouseEnter).toHaveBeenCalledTimes(1); - expect(onMouseEnter).toHaveBeenLastCalledWith(expect.objectContaining({ type: 'mouseenter' })); + expectLastCalledWith(onMouseEnter, expect.objectContaining({ type: 'mouseenter' }));test/chart/Treemap.spec.tsx (1)
373-470: Consider extracting the sharedrootobject to reduce duplication.The
expectedTreemapNode/expectedNodeobjects in theonMouseEnter(Line 373),onMouseLeave(Line 518), andonClick(Line 646) tests all contain the identicalrootstructure (~80 lines). Extracting it to a shared constant at thedescribelevel would cut ~160 lines of duplication and make future maintenance easier.That said, the refactor from inline literals to typed
TreemapNodeconstants is a clear improvement over the previous approach.src/chart/Treemap.tsx (1)
461-465: Narrowing toSVGGraphicsElementis a good improvement for type safety.The event target correctly reflects that Treemap's
Layerrenders SVG<g>elements (which extendSVGGraphicsElement).Note the asymmetry:
onMouseEnterandonMouseLeavepass both(node, event)whileonClickonly passes(node)— no event. This is pre-existing behavior (confirmed by the test on line 644 of Treemap.spec.tsx), but it may surprise consumers. Consider adding a JSDoc note ononClickexplaining this, or filing a follow-up to include the event parameter for consistency.src/index.ts (1)
73-73: ExportingInternalRadarPropsas public API — is the naming intentional?The prefix "Internal" in a publicly exported type may confuse consumers into thinking it's not part of the stable API. Since this type describes the payload shape passed to Radar event handlers (e.g.,
onMouseEnter), a name likeRadarEventPayloadorRadarHandlerPropsmight better communicate its purpose.If the name is intentional and temporary, consider adding a JSDoc note on the type definition in
src/polar/Radar.tsxclarifying its role and stability.test/cartesian/ReferenceDot.spec.tsx (1)
414-438: PreferexpectLastCalledWithhelper overexpect(spy).toHaveBeenLastCalledWith(...).Line 410 correctly uses
expectLastCalledWith, but the subsequent assertions on lines 414, 416, 420, 422, 426, 430, 434, and 438 fall back toexpect(spy).toHaveBeenLastCalledWith(...). For consistency and the better typing/autocompletion the helper provides, consider usingexpectLastCalledWiththroughout.Example fix for one assertion
- expect(onMouseEnter).toHaveBeenLastCalledWith(expectedDotProps, expect.objectContaining({ type: 'mouseenter' })); + expectLastCalledWith(onMouseEnter, expectedDotProps, expect.objectContaining({ type: 'mouseenter' }));As per coding guidelines: "Use the
expectLastCalledWithhelper function instead ofexpect(spy).toHaveBeenLastCalledWith(...)for better typing and autocompletion"
| onMouseLeave?: (dotProps: DotProps, e: React.MouseEvent<SVGCircleElement>) => void; | ||
| } | ||
|
|
||
| export type Props = Omit<DotProps, 'cx' | 'cy' | 'clipDot' | 'dangerouslySetInnerHTML'> & ReferenceDotProps; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the Dot.tsx file
fd 'Dot\.tsx' --type f | head -10Repository: recharts/recharts
Length of output: 153
🏁 Script executed:
# Also find ReferenceDot.tsx to see the full context
fd 'ReferenceDot\.tsx' --type f | head -10Repository: recharts/recharts
Length of output: 91
🏁 Script executed:
# Search for DotProps definition
rg 'type DotProps|interface DotProps' --type ts --type tsx -A 15Repository: recharts/recharts
Length of output: 88
🏁 Script executed:
# Search for ReferenceDotProps definition
rg 'type ReferenceDotProps|interface ReferenceDotProps' --type ts --type tsx -A 15Repository: recharts/recharts
Length of output: 88
🏁 Script executed:
# Search for DotProps definition without file type restrictions
rg 'type DotProps|interface DotProps' -A 20Repository: recharts/recharts
Length of output: 832
🏁 Script executed:
# Search for ReferenceDotProps definition
rg 'type ReferenceDotProps|interface ReferenceDotProps' -A 20Repository: recharts/recharts
Length of output: 1423
🏁 Script executed:
# Read the Dot.tsx file to see DotProps
cat src/shape/Dot.tsx | head -150Repository: recharts/recharts
Length of output: 1370
🏁 Script executed:
# Read ReferenceDot.tsx to see the Props definition and context
cat src/cartesian/ReferenceDot.tsx | head -150Repository: recharts/recharts
Length of output: 5611
🏁 Script executed:
# Find PresentationAttributesWithProps definition
rg 'type PresentationAttributesWithProps|interface PresentationAttributesWithProps' -A 10Repository: recharts/recharts
Length of output: 712
🏁 Script executed:
# Also check the adaptEventHandlers function to understand how handlers are managed
rg 'export.*adaptEventHandlers|function adaptEventHandlers' -A 20Repository: recharts/recharts
Length of output: 1040
🏁 Script executed:
# Check SVGProps or similar to understand the standard event handlers
rg 'SVGProps|svgPropertiesNoEvents' -B 5 -A 10Repository: recharts/recharts
Length of output: 50374
🏁 Script executed:
# Find DOMAttributesWithProps definition
rg 'type DOMAttributesWithProps|interface DOMAttributesWithProps' -A 10Repository: recharts/recharts
Length of output: 594
🏁 Script executed:
# Check the full types.ts file for event handler related types
rg 'DOMAttributesWithProps|DOMAttributesAdaptChildEvent' -B 2 -A 10Repository: recharts/recharts
Length of output: 2345
🏁 Script executed:
# See how the Props type is used in ReferenceDot
cat src/cartesian/ReferenceDot.tsx | tail -200 | head -150Repository: recharts/recharts
Length of output: 4701
🏁 Script executed:
# Check if there are any issues reported in tests or if this is actually causing problems
rg 'ReferenceDot' src/cartesian/__tests__/ -l | head -5Repository: recharts/recharts
Length of output: 125
🏁 Script executed:
# Get the complete list of event handlers from DOMAttributesWithProps
rg 'export interface DOMAttributesWithProps' -A 200 | head -250Repository: recharts/recharts
Length of output: 11465
🏁 Script executed:
# Check if TypeScript compilation has any errors related to ReferenceDot
rg 'Cannot assign' src/cartesian/ReferenceDot.tsxRepository: recharts/recharts
Length of output: 43
🏁 Script executed:
# Look for any type assertion or ts-ignore comments around the Props definition
rg '@ts-expect-error|@ts-ignore' src/cartesian/ReferenceDot.tsxRepository: recharts/recharts
Length of output: 113
Omit the conflicting mouse event handler keys from DotProps.
Props intersects Omit<DotProps, ...> with ReferenceDotProps, which redefines mouse event handlers (onClick, onMouseDown, onMouseUp, onMouseOver, onMouseOut, onMouseEnter, onMouseMove, onMouseLeave) with a different signature: (dotProps: DotProps, e: React.MouseEvent<SVGCircleElement>) => void. Since DotProps includes these handlers from DOMAttributesWithProps with the RechartsMouseEventHandler signature, the intersection creates conflicting overloads. Add these event handler keys to the Omit list to resolve the type conflict.
🤖 Prompt for AI Agents
In `@src/cartesian/ReferenceDot.tsx` at line 128, The Props type currently
intersects Omit<DotProps, 'cx' | 'cy' | 'clipDot' | 'dangerouslySetInnerHTML'>
with ReferenceDotProps, causing conflicting mouse event handler signatures;
update the Omit on DotProps in the Props declaration to also exclude the mouse
event handler keys (onClick, onMouseDown, onMouseUp, onMouseOver, onMouseOut,
onMouseEnter, onMouseMove, onMouseLeave) so ReferenceDotProps' handler types are
used (adjust the Omit list in the Props type near the ReferenceDot.tsx export).
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6970 +/- ##
=======================================
Coverage 90.38% 90.38%
=======================================
Files 515 515
Lines 38497 38501 +4
Branches 5338 5338
=======================================
+ Hits 34797 34801 +4
Misses 3691 3691
Partials 9 9 ☔ 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 ReportBundle size has no change ✅ |
Description
Added tests to make sure that the new function we added in #6942 is internally compatible with our type handlers.
Related Issue
#6645
Fixes #3362
Fixes #4872
Closes #5495
Motivation and Context
I suspect that the new functionality and hooks will draw more attention to the event handlers so I would like this to be easy to use.
Types of changes
Checklist:
Summary by CodeRabbit
Release Notes
New Features
SankeyElementType,RadarPoint,InternalRadarProps, andScatterPointItemfor improved TypeScript support and better type inference in event handlers.Bug Fixes
TreemapNode.rootoptional to prevent structure validation errors.Refactor
onClickhandler from Dot component.