Files
cve-dashboard/.kiro/specs/reporting-row-visibility/tasks.md

9.2 KiB

Implementation Plan: Reporting Row Visibility

Overview

This plan implements row-level visibility controls for the Reporting page's findings table. All changes are contained within frontend/src/components/pages/ReportingPage.js — no new files, no backend changes. The implementation adds hidden row state management (localStorage-persisted), a visibility filtering step in the data pipeline, selection checkboxes with bulk hide, a Row Visibility Manager popover, chart/export integration, and per-row hide buttons. Each task builds incrementally on the previous one, wiring everything together by the final step.

Tasks

  • 1. Add hidden row state management and localStorage helpers

    • Add the HIDDEN_ROWS_KEY constant ('steam_findings_hidden_rows')
    • Implement loadHiddenRows() function that reads from localStorage, parses JSON, returns a Set<string> (empty set on parse failure or missing key)
    • Implement saveHiddenRows(hiddenSet) function that serializes the set to a JSON array and writes to localStorage (silent catch on failure)
    • Add hiddenRowIds state initialized via useState(loadHiddenRows)
    • Implement hideRow(findingId) callback that adds a string ID to the set and persists
    • Implement restoreRow(findingId) callback that removes a string ID from the set and persists
    • Implement restoreAllRows() callback that clears the set and persists an empty set
    • Requirements: 1.1, 2.1, 2.2, 2.3, 2.4
  • * 1.1 Write property test: localStorage round-trip preserves hidden row state

    • Property 2: localStorage round-trip preserves hidden row state
    • Generate arbitrary sets of valid Finding_ID strings, call saveHiddenRows then loadHiddenRows, assert the returned set contains exactly the same elements
    • Validates: Requirements 2.1, 2.2
  • * 1.2 Write property test: corrupted localStorage produces empty set

    • Property 3: Corrupted localStorage produces empty set
    • Generate arbitrary strings that are not valid JSON arrays of strings, set them in localStorage under the hidden rows key, call loadHiddenRows, assert the result is an empty set and no error is thrown
    • Validates: Requirements 2.4
  • 2. Insert visibility filtering into the data pipeline

    • Add visibleFindings useMemo that filters findings by excluding any finding whose String(f.id) is in hiddenRowIds (short-circuit when set is empty)
    • Modify the existing filtered useMemo to start from visibleFindings instead of findings
    • Ensure column filter dropdowns, action filter, and EXC filter all operate on the post-hide dataset
    • Requirements: 1.2, 5.1, 5.2
  • * 2.1 Write property test: hidden row filtering invariant

    • Property 1: Hidden row filtering invariant
    • Generate arbitrary arrays of finding objects and arbitrary sets of hidden Finding_IDs, compute visibleFindings, assert no finding in the output has an ID present in the hidden set
    • Validates: Requirements 1.1, 1.2, 4.1, 4.3, 5.1, 5.2, 6.1, 6.2
  • 3. Integrate hidden rows with chart and export

    • Pass visibleFindings (instead of findings) to the ActionCoverageDonut component's findings prop
    • Modify the CSV export function to use the sorted/filtered visible rows (already derived from visibleFindings via the pipeline)
    • Modify the XLSX export function to use the sorted/filtered visible rows
    • Verify that chart segment click filtering operates on the visible set
    • Requirements: 4.1, 4.2, 4.3, 6.1, 6.2
  • 4. Checkpoint — Verify core hide/restore pipeline

    • Ensure all tests pass, ask the user if questions arise.
  • 5. Add selection state and bulk hide logic

    • Add selectedRowIds state as useState(new Set())
    • Implement toggleRowSelection(findingId) callback that adds/removes a string ID from the selection set
    • Implement toggleSelectAll() callback that selects all visible sorted row IDs when not all are selected, or clears selection when all are selected
    • Implement hideSelectedRows() callback that unions selectedRowIds into hiddenRowIds, persists, and clears the selection set
    • Add a useEffect that prunes selectedRowIds to only include IDs present in the current sorted array whenever sorted changes
    • Requirements: 8.1, 8.2, 8.3, 8.5, 8.6, 8.8
  • * 5.1 Write property test: bulk hide produces union of hidden and selected sets

    • Property 5: Bulk hide produces the union of hidden and selected sets
    • Generate two arbitrary sets of Finding_ID strings (hidden and selected), simulate hideSelectedRows, assert the resulting hidden set equals the union and the selection set is empty
    • Validates: Requirements 8.5, 8.6
  • * 5.2 Write property test: selection is always a subset of visible rows

    • Property 6: Selection is always a subset of visible rows
    • Generate arbitrary selection and visible row sets, simulate the pruning effect, assert the resulting selection is a subset of visible row IDs
    • Validates: Requirements 8.8
  • * 5.3 Write property test: select all produces exactly the visible row ID set

    • Property 7: Select all produces exactly the visible row ID set
    • Generate an arbitrary array of finding objects representing sorted visible rows, simulate toggleSelectAll from empty selection, assert the selection equals the full visible ID set; toggle again, assert empty
    • Validates: Requirements 8.2, 8.3
  • * 5.4 Write property test: restore removes exactly the specified ID

    • Property 4: Restore removes exactly the specified ID
    • Generate a non-empty set of hidden Finding_IDs, pick a random element, simulate restoreRow, assert the result equals the original set minus that single ID
    • Validates: Requirements 3.3
  • 6. Add selection checkbox column and select-all checkbox to the table

    • Import Square, CheckSquare, and MinusSquare icons from lucide-react
    • Add a fixed 36px selection checkbox column as the first column in the table header and body
    • Render Select_All_Checkbox in the header: CheckSquare when all selected, MinusSquare when partially selected, Square when none selected; onClick calls toggleSelectAll
    • Render Selection_Checkbox on each row: CheckSquare when selected, Square when not; onClick calls toggleRowSelection(finding.id)
    • Style checkboxes with muted slate default color, accent blue when checked/active, matching existing icon sizing
    • Requirements: 8.1, 8.2, 8.3, 8.9, 8.11
  • 7. Add per-row hide button column

    • Add a fixed 36px hide button column as the second column (after selection checkbox) in the table header and body
    • Render an EyeOff icon button on each row; onClick calls hideRow(finding.id)
    • Style the button with 13px icon size, muted slate default color, accent blue on hover, matching existing toolbar icon patterns
    • The column header cell is empty (no label)
    • Requirements: 1.1, 1.3, 1.4, 7.1
  • 8. Implement BulkHideToolbar component

    • Create inline BulkHideToolbar component accepting count, onHide, and onClear props
    • Render "{count} rows selected" label, "Hide Selected" button with EyeOff icon, and "Clear" button
    • Style with dark gradient background, accent border, monospace font, matching existing toolbar patterns
    • Render the toolbar above the table inside the scroll container, only when selectedRowIds.size > 0
    • Wire onHide to hideSelectedRows and onClear to clearing the selection set
    • Requirements: 8.4, 8.5, 8.6, 8.7, 8.10, 8.11
  • 9. Checkpoint — Verify selection and bulk hide UI

    • Ensure all tests pass, ask the user if questions arise.
  • 10. Implement RowVisibilityManager popover component

    • Create inline RowVisibilityManager component accepting hiddenRowIds, findings, onRestore, and onRestoreAll props
    • Add open state for popover visibility, with outside-click-to-close behavior (same pattern as existing ColumnManager)
    • Render a toolbar button with EyeOff icon and "Hidden (N)" count text, styled to match the existing ColumnManager and Queue toolbar buttons (same padding, border radius, font size, uppercase text)
    • When open, render a popover panel listing hidden findings by Finding_ID and title (looked up from the full findings array)
    • Each entry has an Eye icon restore button that calls onRestore(findingId)
    • Include a "Restore All" button at the bottom that calls onRestoreAll
    • When hiddenRowIds.size === 0, show "No rows hidden" message in the popover
    • Use dark gradient background, accent border, and box shadow matching the ColumnManager popover
    • Place the button in the toolbar div adjacent to the ColumnManager button
    • Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 7.2, 7.3
  • 11. Final checkpoint — Verify complete feature integration

    • Ensure all tests pass, ask the user if questions arise.

Notes

  • Tasks marked with * are optional and can be skipped for faster MVP
  • All changes are contained within frontend/src/components/pages/ReportingPage.js — no new files needed
  • The design uses JavaScript throughout; fast-check is the PBT library
  • Each task references specific requirements for traceability
  • Checkpoints ensure incremental validation
  • Property tests validate the 7 correctness properties defined in the design document
  • Unit tests validate specific UI rendering scenarios and edge cases