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_KEYconstant ('steam_findings_hidden_rows') - Implement
loadHiddenRows()function that reads from localStorage, parses JSON, returns aSet<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
hiddenRowIdsstate initialized viauseState(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
- Add the
-
* 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
saveHiddenRowsthenloadHiddenRows, 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
visibleFindingsuseMemo that filtersfindingsby excluding any finding whoseString(f.id)is inhiddenRowIds(short-circuit when set is empty) - Modify the existing
filtereduseMemo to start fromvisibleFindingsinstead offindings - Ensure column filter dropdowns, action filter, and EXC filter all operate on the post-hide dataset
- Requirements: 1.2, 5.1, 5.2
- Add
-
* 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 offindings) to theActionCoverageDonutcomponent'sfindingsprop - Modify the CSV export function to use the sorted/filtered visible rows (already derived from
visibleFindingsvia 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
- Pass
-
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
selectedRowIdsstate asuseState(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 unionsselectedRowIdsintohiddenRowIds, persists, and clears the selection set - Add a
useEffectthat prunesselectedRowIdsto only include IDs present in the currentsortedarray wheneversortedchanges - Requirements: 8.1, 8.2, 8.3, 8.5, 8.6, 8.8
- Add
-
* 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
toggleSelectAllfrom 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, andMinusSquareicons from lucide-react - Add a fixed 36px selection checkbox column as the first column in the table header and body
- Render
Select_All_Checkboxin the header:CheckSquarewhen all selected,MinusSquarewhen partially selected,Squarewhen none selected; onClick callstoggleSelectAll - Render
Selection_Checkboxon each row:CheckSquarewhen selected,Squarewhen not; onClick callstoggleRowSelection(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
- Import
-
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
EyeOfficon button on each row; onClick callshideRow(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
BulkHideToolbarcomponent acceptingcount,onHide, andonClearprops - Render "{count} rows selected" label, "Hide Selected" button with
EyeOfficon, 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
onHidetohideSelectedRowsandonClearto clearing the selection set - Requirements: 8.4, 8.5, 8.6, 8.7, 8.10, 8.11
- Create inline
-
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
RowVisibilityManagercomponent acceptinghiddenRowIds,findings,onRestore, andonRestoreAllprops - Add
openstate for popover visibility, with outside-click-to-close behavior (same pattern as existingColumnManager) - Render a toolbar button with
EyeOfficon 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
findingsarray) - Each entry has an
Eyeicon restore button that callsonRestore(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
- Create inline
-
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