Commit e6d0632
feat: Make Predict Charts Interactive (#22402)
# Interactive Chart for Predict Markets
## Overview
This PR adds interactive drag-to-view functionality to the Predict
market details chart, allowing users to explore historical price data by
dragging their finger across the chart.
CHANGELOG entry: null
https://github.com/user-attachments/assets/036a225c-d751-4233-8e8a-453c03de7506
## Features
### 1. **Touch Interaction**
- Users can touch and drag on the chart to view values at different
points in time
- Crosshair line follows finger position from top to bottom of chart
- Values update in real-time as user drags
### 2. **Visual Feedback**
- **Crosshair Line**: Solid vertical line (theme-aware) extends from top
to bottom
- **Time Label**: Displayed at top of chart showing timestamp at dragged
position
- **Data Point Indicators**: Colored circles on each line at the active
position
- **Value Labels**: Colored badges showing series name and value at each
data point
### 3. **Smart Label Positioning**
- **Collision Detection**: Labels automatically adjust to prevent
overlap when lines cross
- **Side Switching**: Labels flip from right to left side when user
drags past chart midpoint
- **Character Limit**: Labels truncate to 25 characters to prevent
overflow
### 4. **Dynamic Legend Values**
- Legend above chart updates to show values at dragged position
- Automatically reverts to latest values when user releases touch
- Provides context for historical data exploration
### 5. **Gesture Handling**
- **Directional Detection**: Distinguishes between horizontal drags
(chart interaction) and vertical scrolls (page scrolling)
- **No Scroll Conflicts**: Parent ScrollView continues to work for
vertical scrolling
- Smart gesture capture prevents interference between chart interaction
and page navigation
### 6. **Theme Support**
- All colors use design tokens for automatic light/dark mode support
- Crosshair line: `colors.border.muted` (gray in light mode, white in
dark mode)
- Text: `colors.text.alternative` (gray in light mode, white in dark
mode)
- Labels: `colors.background.default` for text on colored backgrounds
## Technical Implementation
### Components Modified
1. **`PredictDetailsChart.tsx`**
- Added `PanResponder` for touch gesture handling
- Implemented collision detection algorithm for label positioning
- Created tooltip overlay with proper z-index stacking
- Added directional gesture discrimination
2. **`ChartLegend.tsx`**
- Added `activeIndex` prop to display dragged position values
- Updates dynamically during drag interaction
- Reverts to latest values on release
### Key Technical Decisions
- **Tooltip Rendering**: Uses separate transparent `LineChart` overlay
rendered last for proper z-index
- **Collision Detection**: Sorts labels by Y position and applies
minimum 4px spacing
- **Gesture Direction**: Uses `gestureState.dx` and `gestureState.dy` to
determine horizontal vs vertical movement
- **Label Truncation**: 25-character limit with ellipsis for long
outcome titles
## Testing
### Manual Testing Steps
1. Navigate to any Predict market with price history
2. Touch and drag horizontally on the chart
3. Verify:
- Crosshair line appears and follows touch
- Time label shows at top of chart
- Value labels appear next to data points
- Legend values update to show dragged position
- Labels flip to left side when dragging on right half
- Labels don't overlap when lines cross
4. Release touch and verify:
- Tooltip disappears
- Legend reverts to latest values
5. Try vertical scrolling on chart area:
- Verify page scrolls normally
- Chart interaction only activates on horizontal drag
### Edge Cases Tested
- ✅ Multiple series with crossing lines (collision detection)
- ✅ Long outcome titles (truncation to 25 chars)
- ✅ Single vs multiple series charts
- ✅ Empty data states
- ✅ Loading states
- ✅ Light and dark modes
- ✅ Gesture conflicts with ScrollView
## Screenshots/Demo
_Add screenshots or screen recording demonstrating the interactive
features_
## Performance Considerations
- Uses `useCallback` for gesture handlers to prevent unnecessary
re-renders
- Tooltip component defined outside render to avoid recreation
- Collision detection runs only during active drag (not on every render)
- Proper cleanup on gesture release
## Accessibility
- Touch targets are appropriate size (chart fills available width)
- Visual feedback is clear and theme-aware
- Falls back to showing latest values when not interacting
## Breaking Changes
None. All changes are additive and backward compatible.
## Dependencies
No new dependencies added. Uses existing:
- `react-native-svg-charts` (already in use)
- `react-native-svg` (already in use)
- React Native `PanResponder` (built-in)
## Future Enhancements
Potential improvements for future PRs:
- Haptic feedback on touch
- Animation transitions for label movements
- Pinch-to-zoom for chart data
- Export chart data feature
- Customizable tooltip appearance
## Commits
- `ba2a015` - feat: add interactive chart with drag-to-view values in
Predict
- `3a40cd1` - refactor: improve chart tooltip styling and positioning
- `93142d5` - feat: add label collision detection to chart tooltip
- `2a6f32b` - feat: update legend values dynamically based on dragged
position
- `a9d37d6` - feat: add character limit to tooltip labels
## Checklist
- [x] Code follows project coding guidelines
- [x] No linting errors
- [x] Changes are backward compatible
- [x] Manual testing completed
- [x] Works in both light and dark themes
- [x] Gesture handling doesn't conflict with ScrollView
- [ ] Unit tests added (if applicable)
- [ ] E2E tests updated (if applicable)
- [ ] Documentation updated (if applicable)
## Related Issues
_Link any related issues or tickets here_
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Adds touch-driven crosshair/tooltip overlay with dynamic legend
updates to Predict details chart, plus extensive tests and minor
type/mocks updates.
>
> - **PredictDetailsChart** (`PredictDetailsChart.tsx`):
> - Add interactive drag support via `PanResponder` with
horizontal/vertical gesture discrimination.
> - Implement `ChartTooltip` overlay (crosshair, timestamp, per-series
circles/labels) rendered via transparent `LineChart` layer.
> - Dynamic legend: pass `activeIndex` to `ChartLegend` to show values
at the dragged position; revert when inactive.
> - Data/type updates: `ChartSeries.data` supports optional `label`;
generate formatted `label` for timestamps; collision handling and
side-switching for tooltip labels; theme-aware colors.
> - **ChartLegend** (`components/ChartLegend.tsx`):
> - Accept `activeIndex` to display contextual values; fallback to last
point; formatting via `formatTickValue`.
> - **Tests**:
> - Expand `PredictDetailsChart.test.tsx` to cover interactions,
overlays, label truncation/collisions, theme, multiple series, and axis
labels; adjust `LineChart` mock to hide transparent overlay; add
`Circle`/`Rect` to SVG mocks.
> - Add `ChartLegend.test.tsx` with value formatting, activeIndex
behaviors, edge cases, and multi-series coverage.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
98d1802. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Luis Taniça <matallui@gmail.com>1 parent 47f8e57 commit e6d0632
File tree
4 files changed
+1000
-15
lines changed- app/components/UI/Predict/components/PredictDetailsChart
- components
4 files changed
+1000
-15
lines changedLines changed: 308 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
| 6 | + | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
8 | 10 | | |
9 | | - | |
10 | | - | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
11 | 15 | | |
12 | 16 | | |
13 | 17 | | |
| |||
43 | 47 | | |
44 | 48 | | |
45 | 49 | | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
46 | 58 | | |
47 | 59 | | |
48 | 60 | | |
| |||
142 | 154 | | |
143 | 155 | | |
144 | 156 | | |
145 | | - | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
146 | 160 | | |
147 | | - | |
| 161 | + | |
148 | 162 | | |
149 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
150 | 167 | | |
151 | 168 | | |
152 | 169 | | |
| |||
390 | 407 | | |
391 | 408 | | |
392 | 409 | | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
393 | 696 | | |
0 commit comments