Skip to content

Conversation

@ehconitin
Copy link
Contributor

@ehconitin ehconitin commented Nov 3, 2025

Issue -

  • tooltip gets too long when groupby has too much data points
  • we need max height on tooltips
  • we need to be able to scroll inside the tooltip
  • not possible if the tooltip is following the cursor (which library enforces)
  • its not easy to customize Nivo's own tooltip to make it work how we want it

what we want -

  • let the tooltip anchor to the bar in such a way -- that the top of the bar aligns with the vertical middle of the tooltip
  • add max height to the tooltip

what I did -

  • use floating portal from floating-ui/react
  • get the hover datum (ie hovered bars) dimensions on mouse enter to render the tooltip
  • anchor the tooltip at the calculated position
  • floating-ui handles the basic things like flipping/offset/shift
  • clear the states as required

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Implemented anchored tooltip functionality for bar charts using Floating UI, replacing the native Nivo tooltip system with a custom implementation that allows users to hover over tooltips.

Key Changes:

  • Created new useBarChartFloatingTooltip hook that manages tooltip positioning using virtual elements and debounced hover states
  • Tooltips now render in a FloatingPortal outside the chart SVG, enabling pointer events and user interaction
  • Added interactive and scrollable props to GraphWidgetTooltip to enable pointer events and handle overflow
  • Removed enableGroupTooltip prop from the public API (now determined automatically based on groupMode)
  • Enriched bar datum with positioning data (barAbsX, barAbsY, etc.) to calculate virtual anchor elements
  • Implemented 100ms debounce to allow smooth transitions between bar and tooltip hover states

Implementation Details:

  • Uses Floating UI's flip and shift middleware for intelligent tooltip placement
  • Virtual element positioning calculates bounding rect from SVG coordinates
  • Tooltip visibility controlled by combined state: isChartHovered || isTooltipHovered

Confidence Score: 4/5

  • Safe to merge with minor cleanup recommended
  • The implementation is well-structured with proper state management and debouncing. The TODO comment on line 14 should be addressed. This is the first usage of FloatingPortal in the codebase, so thorough testing is recommended to ensure proper behavior across different scenarios.
  • Check useBarChartFloatingTooltip.ts for the TODO comment to be resolved

Important Files Changed

File Analysis

Filename Score Overview
packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartFloatingTooltip.ts 4/5 New hook implementing floating tooltip with virtual element positioning; contains TODO comment to address
packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx 5/5 Integrated floating tooltip system, removed enableGroupTooltip prop, tooltip now renders in portal with proper event handling
packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetTooltip.tsx 5/5 Added interactive and scrollable props to support anchored tooltip with pointer events and overflow handling

Sequence Diagram

sequenceDiagram
    participant User
    participant CustomBarItem
    participant GraphWidgetBarChart
    participant useBarChartFloatingTooltip
    participant FloatingUI
    participant FloatingPortal
    participant GraphWidgetTooltip

    User->>CustomBarItem: Mouse enters bar
    CustomBarItem->>CustomBarItem: Enrich bar datum with position data
    CustomBarItem->>GraphWidgetBarChart: onMouseEnter(enrichedDatum)
    GraphWidgetBarChart->>useBarChartFloatingTooltip: handleBarMouseEnter(enrichedDatum)
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Cancel debounce timer
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Set isChartHovered = true
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Set hoveredDatum
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Create virtual element from bar position
    useBarChartFloatingTooltip->>FloatingUI: refs.setReference(virtualElement)
    FloatingUI->>FloatingUI: Calculate tooltip position with flip/shift
    useBarChartFloatingTooltip-->>GraphWidgetBarChart: isTooltipVisible = true
    GraphWidgetBarChart->>FloatingPortal: Render tooltip in portal
    FloatingPortal->>GraphWidgetTooltip: Render with interactive=true, scrollable=true
    GraphWidgetTooltip-->>User: Display anchored tooltip

    User->>CustomBarItem: Mouse leaves bar
    CustomBarItem->>GraphWidgetBarChart: onMouseLeave()
    GraphWidgetBarChart->>useBarChartFloatingTooltip: handleBarMouseLeave()
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Debounce setChartHovered(false)
    
    alt User moves to tooltip within 100ms
        User->>GraphWidgetTooltip: Mouse enters tooltip
        GraphWidgetTooltip->>useBarChartFloatingTooltip: handleTooltipMouseEnter()
        useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Cancel debounce, setIsTooltipHovered = true
        useBarChartFloatingTooltip-->>GraphWidgetBarChart: isTooltipVisible = true
        Note over GraphWidgetTooltip: Tooltip stays visible
    else Timeout expires
        useBarChartFloatingTooltip->>useBarChartFloatingTooltip: setIsChartHovered = false
        useBarChartFloatingTooltip-->>GraphWidgetBarChart: isTooltipVisible = false
        GraphWidgetBarChart->>FloatingPortal: Unmount tooltip
    end

    User->>GraphWidgetTooltip: Mouse leaves tooltip
    GraphWidgetTooltip->>useBarChartFloatingTooltip: handleTooltipMouseLeave()
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Set isTooltipHovered = false
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Set hoveredDatum = null
    useBarChartFloatingTooltip-->>GraphWidgetBarChart: isTooltipVisible = false
    GraphWidgetBarChart->>FloatingPortal: Unmount tooltip
Loading

7 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@github-actions
Copy link
Contributor

github-actions bot commented Nov 3, 2025

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:27419

This environment will automatically shut down when the PR is closed or after 5 hours.

@ehconitin ehconitin self-assigned this Nov 3, 2025
@lucasbordeau
Copy link
Contributor

Could you provide a description in your PR and also link an existing issue ?

@lucasbordeau
Copy link
Contributor

@Bonapara I suggest that we put in bold the line in the tooltip that corresponds to the hovered group, example :

Capture d’écran 2025-11-04 à 11 34 41

@Bonapara
Copy link
Member

Bonapara commented Nov 4, 2025

Great idea @lucasbordeau - medium + darker colors (secondary for the label & tertiary for the value)

@ehconitin
Copy link
Contributor Author

@lucasbordeau mb, updated the description!

and will push the new requirements shortly after

@ehconitin
Copy link
Contributor Author

@greptileai

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Replaces Nivo's cursor-following tooltip with an anchored tooltip using floating-ui. The tooltip now anchors to the bar element itself, enabling scrolling for long content lists without following the cursor.

Key changes:

  • New useBarChartFloatingTooltip hook manages tooltip positioning using floating-ui's virtual element API
  • Tooltip stays anchored at bar position with smart placement (flip/shift middleware)
  • Added 300ms debounced hide to allow users to hover into the tooltip for scrolling
  • Tooltip supports max-height (120px) with overflow scrolling when scrollable prop is enabled
  • Removed enableGroupTooltip prop - now automatically enabled for stacked mode
  • Bar geometry data (position, dimensions) passed through mouse events for accurate positioning

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • Well-structured refactoring that replaces library tooltip with custom implementation. Clean separation of concerns with dedicated hook, proper debouncing, existing floating-ui dependency, and backward-compatible changes.
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartFloatingTooltip.ts 5/5 New hook implementing floating-ui based tooltip positioning with debounced hide logic and virtual element anchoring
packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx 5/5 Refactored to use custom floating tooltip instead of Nivo's built-in tooltip, added FloatingPortal with mouse event handlers
packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetTooltip.tsx 5/5 Added interactive and scrollable props, implemented highlighting for hovered items, and added key field to tooltip items

Sequence Diagram

sequenceDiagram
    participant User
    participant CustomBarItem
    participant GraphWidgetBarChart
    participant useBarChartFloatingTooltip
    participant FloatingUI
    participant GraphWidgetTooltip

    User->>CustomBarItem: Mouse enters bar
    CustomBarItem->>CustomBarItem: Gather bar geometry data
    Note over CustomBarItem: barElement, barX, barY,<br/>barWidth, barHeight, etc.
    CustomBarItem->>GraphWidgetBarChart: onMouseEnter(barDatumWithGeometry)
    GraphWidgetBarChart->>useBarChartFloatingTooltip: showTooltipForBar(datum)
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Cancel any pending hide
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: createFloatingAnchorFromBarGeometry()
    useBarChartFloatingTooltip->>FloatingUI: refs.setReference(virtualElement)
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: setHoveredBarDatum(datum)
    FloatingUI->>FloatingUI: Calculate position with flip/shift/offset
    GraphWidgetBarChart->>GraphWidgetTooltip: Render tooltip in FloatingPortal
    GraphWidgetTooltip-->>User: Display anchored tooltip

    User->>CustomBarItem: Mouse leaves bar
    CustomBarItem->>GraphWidgetBarChart: onMouseLeave()
    GraphWidgetBarChart->>useBarChartFloatingTooltip: scheduleTooltipHide()
    useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Start 300ms debounced timer

    alt User hovers tooltip before timer expires
        User->>GraphWidgetTooltip: Mouse enters tooltip
        GraphWidgetTooltip->>GraphWidgetBarChart: onMouseEnter
        GraphWidgetBarChart->>useBarChartFloatingTooltip: cancelTooltipHide()
        useBarChartFloatingTooltip->>useBarChartFloatingTooltip: Cancel debounced timer
        Note over GraphWidgetTooltip: Tooltip remains visible<br/>and scrollable
        User->>GraphWidgetTooltip: Mouse leaves tooltip
        GraphWidgetTooltip->>GraphWidgetBarChart: onMouseLeave
        GraphWidgetBarChart->>useBarChartFloatingTooltip: hideTooltip()
        useBarChartFloatingTooltip->>useBarChartFloatingTooltip: setHoveredBarDatum(null)
    else Timer expires
        useBarChartFloatingTooltip->>useBarChartFloatingTooltip: setHoveredBarDatum(null)
    end
    
    GraphWidgetTooltip-->>User: Tooltip disappears
Loading

7 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@ehconitin
Copy link
Contributor Author

@greptileai

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Overview

Greptile Summary

Replaced Nivo's cursor-following tooltips with anchored tooltips using floating-ui to enable scrollable content for charts with many data points.

Key Changes:

  • Introduced GraphWidgetFloatingTooltip component that uses floating-ui's positioning system with flip/shift middleware for intelligent placement
  • Added useTooltipFloating hook to wrap floating-ui configuration with chart-specific settings
  • Updated GraphWidgetTooltip to support scrollable content (max height 120px) and highlighting for multi-series data
  • Refactored bar charts to pass anchor element references instead of using Nivo's built-in tooltip system
  • Created CustomCrosshairLayer for line charts to handle mouse tracking and tooltip positioning
  • Added utility functions to create virtual elements from SVG elements and coordinates for floating-ui positioning
  • Implemented debounced tooltip hiding (300ms) with ability to cancel when hovering over tooltip
  • Changed tooltip pointer-events from none to auto to enable interaction with clickable links

Technical Approach:

  • Bar charts anchor tooltips to the actual bar SVG elements
  • Line charts create virtual elements from calculated offset positions
  • Tooltips remain anchored while allowing scroll within content area
  • Error handling added for missing container elements

Confidence Score: 5/5

  • This PR is safe to merge - well-architected refactoring with proper error handling and no breaking changes
  • The implementation is clean, follows React best practices, includes proper error handling for missing DOM elements, uses established libraries (floating-ui), maintains existing functionality while adding new capabilities, and improves UX for charts with many data points. The code is well-structured with clear separation of concerns.
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetFloatingTooltip.tsx 5/5 introduced new floating tooltip component using floating-ui for positioning, handles both bar chart and line chart anchor types with error handling
packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetTooltip.tsx 5/5 updated tooltip to support scrollable content with max height, highlighting, proper key-based rendering, and changed pointer-events to auto for interactivity
packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx 5/5 refactored to use new anchored tooltip system with state management, debounced hiding, and passes anchor element reference on mouse enter events
packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChart.tsx 5/5 integrated new anchored tooltip with crosshair layer, uses custom crosshair layer for better tooltip positioning control
packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetLineChart/components/CustomCrosshairLayer.tsx 5/5 added new custom crosshair layer with animated vertical line, handles mouse movement to find nearest slice and closest point for tooltip anchoring

Sequence Diagram

sequenceDiagram
    participant User
    participant BarChart
    participant CustomBarItem
    participant LineChart
    participant CustomCrosshairLayer
    participant FloatingTooltip
    participant FloatingUI
    participant TooltipComponent

    Note over User,TooltipComponent: Bar Chart Interaction Flow
    User->>BarChart: Mouse enters bar
    BarChart->>CustomBarItem: onMouseEnter event
    CustomBarItem->>BarChart: Passes event to parent handler
    BarChart->>BarChart: getTooltipData(datum)
    BarChart->>BarChart: setActiveTooltipData with bar-element-anchor
    BarChart->>FloatingTooltip: Render with tooltipData
    FloatingTooltip->>FloatingTooltip: getTooltipReferenceFromBarChartElementAnchor
    FloatingTooltip->>FloatingUI: useFloating with reference element
    FloatingUI->>FloatingUI: Calculate position with flip/shift
    FloatingUI-->>FloatingTooltip: Return position styles
    FloatingTooltip->>TooltipComponent: Render with items & highlighting
    TooltipComponent-->>User: Display anchored tooltip

    Note over User,TooltipComponent: Line Chart Interaction Flow
    User->>LineChart: Mouse moves over chart
    LineChart->>CustomCrosshairLayer: onMouseMove event
    CustomCrosshairLayer->>CustomCrosshairLayer: Find nearest slice & closest point
    CustomCrosshairLayer->>LineChart: onSliceHover with hover data
    LineChart->>LineChart: createSliceTooltipData
    LineChart->>LineChart: setActiveTooltipData with line-point-anchor
    LineChart->>LineChart: setCrosshairX for visual indicator
    LineChart->>FloatingTooltip: Render with tooltipData
    FloatingTooltip->>FloatingTooltip: getTooltipReferenceFromLineChartPointAnchor
    FloatingTooltip->>FloatingTooltip: createVirtualElementFromContainerOffset
    FloatingTooltip->>FloatingUI: useFloating with virtual element
    FloatingUI->>FloatingUI: Calculate position with flip/shift
    FloatingUI-->>FloatingTooltip: Return position styles
    FloatingTooltip->>TooltipComponent: Render with items & highlighting
    TooltipComponent-->>User: Display anchored tooltip with crosshair

    Note over User,TooltipComponent: Tooltip Interaction & Hiding
    User->>TooltipComponent: Mouse enters tooltip
    TooltipComponent->>FloatingTooltip: onMouseEnter
    FloatingTooltip->>BarChart: onCancelScheduledHide
    BarChart->>BarChart: Cancel debounced hide
    User->>TooltipComponent: Mouse leaves tooltip
    TooltipComponent->>FloatingTooltip: onMouseLeave
    FloatingTooltip->>BarChart: onScheduleHide
    BarChart->>BarChart: Schedule debounced hide (300ms)
    BarChart->>BarChart: setActiveTooltipData(null)
    BarChart->>FloatingTooltip: Unmount tooltip
Loading

44 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@lucasbordeau lucasbordeau left a comment

Choose a reason for hiding this comment

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

Overall that's better yes, we could refine the behavior of the tooltips with Bonapara later, but for now let's focus on the code, I think that you can push it a little bit further.

@@ -0,0 +1 @@
export const LINE_CHART_MARGIN_TOP = 20;
Copy link
Member

Choose a reason for hiding this comment

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

polish: we could group these into one file:
export const LINE_CHART_MARGINS = { right: / top: }

@charlesBochet charlesBochet merged commit 5612974 into main Nov 11, 2025
64 checks passed
@charlesBochet charlesBochet deleted the tooltip-anchor branch November 11, 2025 15:47
@github-actions
Copy link
Contributor

Thanks @ehconitin for your contribution!
This marks your 245th PR on the repo. You're top 1% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants