Fix/tooltip deactivate outside cartesian plot#7115
Fix/tooltip deactivate outside cartesian plot#7115AbishekRaj2007 wants to merge 3 commits intorecharts:mainfrom
Conversation
WalkthroughThis change adds detection for clicks outside the SVG/plot bounds in the mouse events middleware, dispatching a new Changes
Sequence DiagramsequenceDiagram
participant User
participant MouseEventsMiddleware
participant ChartOffsetSelector
participant TooltipReducer
participant AppState
User->>MouseEventsMiddleware: Click on SVG/Chart
MouseEventsMiddleware->>ChartOffsetSelector: Get chart offset and dimensions
ChartOffsetSelector-->>MouseEventsMiddleware: Return innerWidth/innerHeight/offset
MouseEventsMiddleware->>MouseEventsMiddleware: Calculate chart-space coords<br/>(chartX, chartY)
MouseEventsMiddleware->>MouseEventsMiddleware: Check if click is outside<br/>inner bounds + margins
alt Click is Outside Plot Bounds
MouseEventsMiddleware->>TooltipReducer: Dispatch mouseClickOutsidePlot()
TooltipReducer->>AppState: Set axisInteraction.click.active = false
AppState-->>TooltipReducer: State updated
else Click is Inside Plot Bounds
MouseEventsMiddleware->>MouseEventsMiddleware: Compute activeProps from<br/>relative coordinates
MouseEventsMiddleware->>TooltipReducer: Dispatch setMouseClickAxisIndex
TooltipReducer->>AppState: Update tooltip state
AppState-->>TooltipReducer: State updated
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 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 Tip CodeRabbit can generate a title for your PR based on the changes.Add |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
test/state/mouseEventsMiddleware.spec.ts (2)
98-106: Missingvi.useFakeTimers()in the new test suite.Per coding guidelines, tests should use
vi.useFakeTimers()due to Redux autoBatchEnhancer dependency on timers. The existingmouseClickMiddlewaredescribe block at line 67 doesn't explicitly call it either, but it referencesvi.getTimerCount()which suggests fake timers are configured globally (likely in a setup file). However, for consistency and explicitness, consider addingbeforeEach(() => vi.useFakeTimers())to ensure the test suite is self-contained.Also, the
afterEachcomment mentions clearing RAF handles from autoBatchEnhancer, which is good practice.💡 Optional: Add explicit fake timers setup
describe('mouseClickMiddleware – plot bounds guard', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + afterEach(() => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/state/mouseEventsMiddleware.spec.ts` around lines 98 - 106, The new "mouseClickMiddleware – plot bounds guard" test suite is missing an explicit call to vi.useFakeTimers(), so add a beforeEach hook inside the describe block (the same block that contains the existing afterEach with vi.clearAllTimers()) to call vi.useFakeTimers() before each test; keep the existing afterEach that clears RAF timers (vi.clearAllTimers()) so autoBatchEnhancer leftovers are cleaned up.
146-166: Test coverage is adequate, but consider adding a note about the test setup.This test correctly verifies that clicking inside the plot area does not dispatch
mouseClickOutsidePlot. However, without tooltip ticks/data configured,selectActivePropsFromChartPointerreturns undefined, sosetMouseClickAxisIndexis never dispatched either. The tooltip remains active simply because no deactivating action occurs.This is fine for testing the bounds guard itself, but a comment clarifying this behavior would help future maintainers understand the test's intent.
📝 Optional: Add clarifying comment
it('should NOT deactivate the tooltip when clicking inside the plot area', () => { const store = createStoreWithChartDimensions(); - // Pre-activate the tooltip + // Pre-activate the tooltip. Note: without tooltip ticks/data, the middleware + // won't dispatch setMouseClickAxisIndex, but the key assertion is that + // mouseClickOutsidePlot is NOT dispatched for in-range clicks. store.dispatch(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/state/mouseEventsMiddleware.spec.ts` around lines 146 - 166, Add an inline clarifying comment in the test (near the pre-activation and the mouseClickAction(INSIDE_PLOT) dispatch) explaining that because no tooltip ticks/data are configured, selectActivePropsFromChartPointer will return undefined so setMouseClickAxisIndex is not dispatched and the tooltip remains active only because no deactivating action occurs; reference the related symbols setMouseClickAxisIndex, selectActivePropsFromChartPointer, mouseClickAction and mouseClickOutsidePlot in the comment so future maintainers understand the test's intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@test/state/mouseEventsMiddleware.spec.ts`:
- Around line 98-106: The new "mouseClickMiddleware – plot bounds guard" test
suite is missing an explicit call to vi.useFakeTimers(), so add a beforeEach
hook inside the describe block (the same block that contains the existing
afterEach with vi.clearAllTimers()) to call vi.useFakeTimers() before each test;
keep the existing afterEach that clears RAF timers (vi.clearAllTimers()) so
autoBatchEnhancer leftovers are cleaned up.
- Around line 146-166: Add an inline clarifying comment in the test (near the
pre-activation and the mouseClickAction(INSIDE_PLOT) dispatch) explaining that
because no tooltip ticks/data are configured, selectActivePropsFromChartPointer
will return undefined so setMouseClickAxisIndex is not dispatched and the
tooltip remains active only because no deactivating action occurs; reference the
related symbols setMouseClickAxisIndex, selectActivePropsFromChartPointer,
mouseClickAction and mouseClickOutsidePlot in the comment so future maintainers
understand the test's intent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 974fcb12-8dfa-4532-b611-bc0b89caff09
📒 Files selected for processing (5)
src/state/keyboardEventsMiddleware.tssrc/state/mouseEventsMiddleware.tssrc/state/tooltipSlice.tstest/chart/AccessibilityLayer.spec.tsxtest/state/mouseEventsMiddleware.spec.ts
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7115 +/- ##
=======================================
Coverage 89.63% 89.63%
=======================================
Files 536 536
Lines 40484 40509 +25
Branches 5523 5528 +5
=======================================
+ Hits 36286 36311 +25
Misses 4190 4190
Partials 8 8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
I can reproduce the same problem even on this branch |
|
Thanks for testing, Could you share which chart type you were using (Line/Bar/Composed) and a minimal reproduction or steps? I want to make sure I'm testing the exact same scenario. |
|
I tried the first I found that has a tooltip: https://recharts.github.io/en-US/api/ I can reproduce both on the main branch, and on your branch |
|
Thanks, I'll test against that example and update the fix. Could you confirm if you're clicking directly on chart margins/padding vs. completely outside the SVG boundary? That'll help me narrow down which coordinate check is failing. |
|
I'm clicking on axis labels |
|
Got it axis labels sit in the margin/offset zone outside the inner plot area. My boundary check was using inner dimensions, so those clicks were being treated as outside. Working on including the offset in the check , will push an update shortly. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/state/mouseEventsMiddleware.ts (1)
29-29: Remove debug logging from middleware path.Line 29 leaves a
console.login production event handling.Suggested fix
- console.log({ chartX, chartY, outerWidth, outerHeight, offset });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/state/mouseEventsMiddleware.ts` at line 29, Remove the leftover console.log call that prints { chartX, chartY, outerWidth, outerHeight, offset } from the middleware path in mouseEventsMiddleware.ts; either delete the console.log or replace it with a proper logger call gated by a debug/verbose flag (e.g., use existing logging utility or process.env.DEBUG) so production doesn't emit these event logs, locating the statement that references chartX, chartY, outerWidth, outerHeight, and offset and removing or wrapping it accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/state/mouseEventsMiddleware.ts`:
- Around line 31-35: The current isOutside check uses innerWidth + offset.right
and innerHeight + offset.bottom which can be larger than the true SVG when
offset.width/height are clamped; instead compare the pointer's relative
coordinates against the actual outer dimensions. Replace the comparisons that
use chartX/chartY vs innerWidth + offset.right and innerHeight + offset.bottom
with checks that compare relativeCoord.x to outerWidth and relativeCoord.y to
outerHeight (or compute outerWidth = innerWidth + offset.left + offset.right and
outerHeight = innerHeight + offset.top + offset.bottom using the true outer
sizes), keeping the existing left/top negative checks (offset.left/offset.top)
so isOutside uses relativeCoord and outerWidth/outerHeight rather than clamped
inner+offset values.
---
Nitpick comments:
In `@src/state/mouseEventsMiddleware.ts`:
- Line 29: Remove the leftover console.log call that prints { chartX, chartY,
outerWidth, outerHeight, offset } from the middleware path in
mouseEventsMiddleware.ts; either delete the console.log or replace it with a
proper logger call gated by a debug/verbose flag (e.g., use existing logging
utility or process.env.DEBUG) so production doesn't emit these event logs,
locating the statement that references chartX, chartY, outerWidth, outerHeight,
and offset and removing or wrapping it accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4b4d23f4-571d-4a8a-a56e-6d696b2e1a30
📒 Files selected for processing (2)
src/state/mouseEventsMiddleware.tstest/state/mouseEventsMiddleware.spec.ts
|
Hi @PavelVanecek, the 33 failing checks appear to be a CI infrastructure issue rather than a problem with this change. All failures occur within ~10 seconds at the pnpm cache restoration step, This seems to be a transient issue with pnpm/action-setup@v4 on GitHub's runners. Could you re-run the failed jobs when you get a chance? |
|
Yeah that started yesterday on all builds. I'll fix it |
|
Thanks for confirming! I've pushed the fix , happy to address any review comments once the CI is back up. |
|
@AbishekRaj2007 can you please rebase on, or merge latest main? I fixed the CI. |
59d7e72 to
1189949
Compare
|
@PavelVanecek I have merged with the latest main and pushed |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
www/src/components/Playground/AnnotationsPanel.tsx (1)
14-16: Update JSDoc to match the new portal/component naming.The comment still refers to the annotations portal/controller, but the component now renders
RechartsDevtoolsPortal. Aligning this text will reduce confusion during maintenance.✏️ Suggested doc-only update
/** - * A panel that renders the Recharts Annotations portal. - * This is the container where the AnnotationsController UI will be rendered. + * A panel that renders the Recharts Devtools portal. + * This is the container where the devtools UI will be rendered. */Based on learnings: When making changes to Recharts code, prefer current best practices as described in CONTRIBUTING.md and try to improve code style where relevant to the current task.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@www/src/components/Playground/AnnotationsPanel.tsx` around lines 14 - 16, Update the JSDoc at the top of the AnnotationsPanel component so it describes rendering the RechartsDevtoolsPortal rather than the old "AnnotationsController" or "annotations portal"; locate the JSDoc block above the AnnotationsPanel component declaration and replace the outdated wording with a concise description that this panel is the container where the RechartsDevtoolsPortal UI will be rendered, keeping tone and style consistent with surrounding comments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@www/src/components/Playground/AnnotationsPanel.tsx`:
- Around line 14-16: Update the JSDoc at the top of the AnnotationsPanel
component so it describes rendering the RechartsDevtoolsPortal rather than the
old "AnnotationsController" or "annotations portal"; locate the JSDoc block
above the AnnotationsPanel component declaration and replace the outdated
wording with a concise description that this panel is the container where the
RechartsDevtoolsPortal UI will be rendered, keeping tone and style consistent
with surrounding comments.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 48a606ea-204e-49e9-b574-92ad4b0e9ae3
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
package.jsonsrc/state/mouseEventsMiddleware.tssrc/state/tooltipSlice.tstest/state/mouseEventsMiddleware.spec.tswww/src/components/Playground/AnnotationsPanel.tsx
💤 Files with no reviewable changes (1)
- package.json
🚧 Files skipped from review as they are similar to previous changes (3)
- src/state/tooltipSlice.ts
- src/state/mouseEventsMiddleware.ts
- test/state/mouseEventsMiddleware.spec.ts
Description
When clicking on XAxis tick labels (which render outside the cartesian plot area), the tooltip was incorrectly activating and displaying data for index 0. This was misleading because the user is interacting with an axis element, not a data point.
Fixes #6961
Root Cause
The
mouseClickMiddlewarehad no bounds check to verify whether the click originated inside the cartesian plot area. When clicking outside (e.g. on XAxis ticks), coordinate resolution failed silently and fell back todefaultIndex ?? 0, triggering a misleading tooltip.Changes
mouseEventsMiddleware.tsisInCartesianRangebounds check inmouseClickMiddlewaremouseClickOutsidePlot()and return earlytooltipSlice.tsmouseClickOutsidePlotreduceraxisInteraction.click.active = falsewhen triggeredmouseEventsMiddleware.spec.tsTesting
mouseEventsMiddleware.spec.tsType of Change
-Bug Fix
Summary by CodeRabbit
Bug Fixes
Tests