5.9 KiB
Implementation Plan: CVE Tooltip Hover
Overview
Implement a hover tooltip for CVE badges in the Reporting Page findings table. The feature spans a backend endpoint (GET /api/cves/:cveId/tooltip) and a frontend CveTooltip portal component with in-memory caching and 300ms hover delay. Tasks are ordered backend-first, then frontend component, then integration, with property tests alongside each layer.
Tasks
-
1. Add backend tooltip endpoint
-
1.1 Add
GET /api/cves/:cveId/tooltiproute inline inbackend/server.js- Place it alongside existing CVE endpoints (after
/api/cves/:cveId/vendors) - Validate
:cveIdagainst existingCVE_ID_PATTERN; return 400 for invalid format - Query:
SELECT cve_id, description, severity FROM cves WHERE cve_id = ? LIMIT 1 - If no row: return
{ exists: false }with status 200 - If row found: truncate
descriptionto 300 chars + "…" if needed, return{ exists: true, cve_id, description, severity } - Protect with
requireAuth(db)middleware - Requirements: 1.1, 1.2, 1.3, 1.4, 1.5
- Place it alongside existing CVE endpoints (after
-
* 1.2 Write property test for tooltip endpoint data correctness
- Property 1: Tooltip endpoint returns correct data for existing CVEs
- Install
fast-checkas dev dependency infrontend/(shared test runner) - Generate random CVE records with description lengths 0–1000 and all 4 severity levels
- Verify response shape, truncation at 300 chars, and prefix preservation
- Validates: Requirements 1.1, 1.3, 1.5
-
* 1.3 Write property test for description truncation
- Property 2: Description truncation preserves content and enforces length
- Extract truncation logic into a testable pure function
- Generate random strings of length 0–2000, verify length invariant and prefix match
- Validates: Requirements 1.5
-
-
2. Checkpoint — Verify backend endpoint
- Ensure all tests pass, ask the user if questions arise.
-
3. Create CveTooltip frontend component
-
3.1 Create
frontend/src/components/CveTooltip.js- Portal-rendered component using
ReactDOM.createPortaltodocument.body - Props:
cveId(string|null),anchorRect(DOMRect|null),cache(useRef Map) - Internal state:
data,loading - On
cveIdchange: check cache → if miss, fetch from/api/cves/:cveId/tooltipwith AbortController - If cached
exists: falseor fetch returnsexists: false, render nothing - Show loading spinner (Loader from lucide-react) while fetching
- Display: CVE ID in monospace, severity badge with design system colors, description text
- Max-width 320px, dark theme gradient background, accent border, directional arrow
- Position above anchor by default; flip below if insufficient viewport space above
- Do not cache transient errors (network failures)
- Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 3.1, 3.2, 3.3, 3.4, 3.5
- Portal-rendered component using
-
* 3.2 Write property test for tooltip positioning logic
- Property 3: Tooltip positioning flips based on available viewport space
- Extract positioning calculation into a pure function
- Generate random anchorRect.top (0–2000), tooltip height (50–200), viewport height (400–1200)
- Verify tooltip never overflows top or bottom of viewport
- Validates: Requirements 3.1, 3.2
-
* 3.3 Write unit tests for CveTooltip component
- Test loading state renders spinner
- Test
exists: falserenders nothing - Test severity badge uses correct color per level
- Test max-width constraint
- Test directional arrow element is present
- Requirements: 2.4, 2.5, 2.6, 3.3, 3.5
-
-
4. Checkpoint — Verify CveTooltip component
- Ensure all tests pass, ask the user if questions arise.
-
5. Integrate tooltip into ReportingPage
-
5.1 Add hover state and cache ref to ReportingPage
- Add state:
tooltipCveId(string|null),tooltipAnchorRect(DOMRect|null) - Add
useRef(new Map())for tooltip cache - Add
useReffor hover delay timer - Clear cache when findings data is re-synced (inside existing sync callback)
- Requirements: 4.1, 4.4, 5.1
- Add state:
-
5.2 Add mouseenter/mouseleave handlers to CVE badge spans
- In the
renderCellfunction for the'cves'column case, wrap each CVE badge<span>withonMouseEnterandonMouseLeave onMouseEnter: start 300ms setTimeout; on fire, settooltipCveIdandtooltipAnchorRectfromgetBoundingClientRect()onMouseLeave: clear timeout, settooltipCveIdto null- Requirements: 2.1, 2.2, 5.1, 5.2
- In the
-
5.3 Render CveTooltip instance in ReportingPage
- Add single
<CveTooltip>at the bottom of the ReportingPage return, passingtooltipCveId,tooltipAnchorRect, and cache ref - Requirements: 2.1, 4.2, 4.3
- Add single
-
* 5.4 Write property test for cache round-trip behavior
- Property 4: Cache round-trip — fetch then cache-hit avoids network call
- Generate random CVE IDs and response payloads, store in Map, verify lookups return identical objects
- Validates: Requirements 4.1, 4.2
-
* 5.5 Write unit tests for hover delay and cache integration
- Test tooltip appears after 300ms delay (use fake timers)
- Test tooltip cancelled if mouseout before 300ms
- Test cached
exists: falsesuppresses tooltip and API call - Test cache cleared on data sync/refresh
- Requirements: 4.3, 4.4, 5.1, 5.2
-
-
6. Final checkpoint — Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
Notes
- Tasks marked with
*are optional and can be skipped for faster MVP - Each task references specific requirements for traceability
- Checkpoints ensure incremental validation
- Property tests validate universal correctness properties from the design document
- Unit tests validate specific examples and edge cases
- The project uses plain JavaScript (no TypeScript), fast-check for PBT, and react-scripts test (Jest)