Implemented bulk selection and sequential cancellation functionality for the StreamsTable component, enabling users to efficiently manage multiple payment streams simultaneously.
-
Checkbox Column: Added as the first column in the table
- Eligibility determined by
isStreamSelectable()— only streams withprogress.status === "active"or"scheduled"are selectable - Completed and canceled streams are not selectable
- Individual toggling handled by
handleCheckboxToggle(streamId), which mutates aSet<string>ofselectedStreamIds
- Eligibility determined by
-
Header Selection: "Select All" checkbox in table header
- Controlled by
handleSelectAllToggle(), driven by the derivedallSelectableSelectedboolean (memoized fromselectableStreams) - Toggling adds/removes all
selectableIdsfromselectedStreamIdsin one update - Automatically reflects checked/unchecked state based on whether every selectable stream is currently selected
- Controlled by
-
State Management:
selectedStreamIds: Set<string>for O(1) lookup performance- A
useEffect(keyed onstreams) prunesselectedStreamIdsdown to only IDs still present in the currentstreamslist, preventing stale selections after refresh/filtering
Implemented via a keydown listener attached to the table's container (tableWrapperRef):
- Ctrl/Cmd + A: Selects/deselects all eligible streams by calling
handleSelectAllToggle(). Ignored when focus is inside a text input (checkboxes are exempted). - Escape: Clears the current selection (
setSelectedStreamIds(new Set())). - Delete / Backspace: When one or more streams are selected, moves focus to the bulk-cancel button (
cancelButtonRef) rather than triggering cancellation directly — this avoids accidental destructive action from a single keypress.
The listener is scoped to the table wrapper element, not the whole document, so shortcuts only fire when the table has focus context.
- Appearance: Shows when
selectedStreamIds.size >= 1 - Position: Fixed at bottom of viewport with high z-index (1000)
- UI Elements:
- Selected count display (e.g., "3 streams selected")
- Prominent "Cancel X Streams" button
- Progress indicator during operation (e.g., "Canceling 3/10...")
- Styling:
- Wave 4 theme compliant (dark background, high contrast)
- Slide-up animation on mount
- Responsive design (centered on desktop, full-width on mobile)
- Execution: Uses
for...ofloop for sequential API calls - Error Handling:
- Failed cancellations are logged but don't stop the sequence
- Continues processing remaining streams after failures
- Post-Action Cleanup:
- Clears
selectedStreamIdsstate - Triggers table data refresh via
onRefreshcallback - Logs success/failure summary to console
- Clears
- No lag when selecting large numbers of rows
- Set-based selection state for efficient lookups
- Sequential API calls prevent backend overload
- Automatic cleanup prevents memory leaks
- Added selection state management
- Implemented checkbox rendering logic
- Created
BulkActionBarcomponent - Added sequential bulk cancellation handler
- Comprehensive inline documentation
- Complete test suite for selection logic
- Tests for "Select All" edge cases
- Integration tests for sequential cancellation
- Progress indicator tests
- Error handling tests
- Added
.bulk-action-barstyles - Slide-up animation keyframes
- Responsive mobile styles
- High-contrast Wave 4 theme colors
-
Added
handleRefreshcallback -
Passed
onRefreshprop to StreamsTable -
Added
tableWrapperRefandcancelButtonRefrefs -
Added keyboard-shortcut
useEffectfor bulk-selection accessibility (Ctrl/Cmd+A, Escape, Delete/Backspace) -
BulkActionBarnow accepts abuttonRefprop so keyboard focus can be moved to the cancel button programmatically
✓ Renders checkboxes only for active/scheduled streams
✓ Selects individual streams on checkbox click
✓ "Select All" selects only eligible streams
✓ "Select All" auto-checks when all manually selected
✓ Deselects all when "Select All" clicked again
✓ Hides "Select All" when no selectable streams exist
✓ Shows bulk action bar when streams selected
✓ Calls cancelStream sequentially for each stream
✓ Shows progress during bulk cancellation
✓ Continues on failure, doesn't stop sequence
✓ Clears selection after completion
✓ Disables button during operation
<StreamsTable
streams={streams}
filters={filters}
onFiltersChange={setFilters}
onCancel={handleCancel}
onEditStartTime={setEditingStream}
onRefresh={handleRefresh} // New prop for bulk refresh
/>The implementation uses sequential execution (for...of loop) rather than parallel (Promise.all()) to:
- Prevent overwhelming the backend with simultaneous requests
- Provide accurate progress tracking
- Ensure predictable execution order
The useEffect hook automatically cleans up selections when:
- Streams are filtered
- Data is refreshed
- Selected streams are removed from the list
This prevents stale selections and ensures UI consistency.
- All checkboxes have proper
aria-labelattributes - Bulk action bar uses semantic HTML
- Progress states are clearly communicated
- Keyboard navigation fully supported
- Toast notifications for success/failure summary
- Undo functionality for bulk cancellations
- Batch API endpoint for improved performance
- Selection persistence across page navigation
- Export selected streams functionality
- Selection state: O(1) lookup time
- No UI lag with 100+ streams
- Sequential cancellation prevents rate limiting
- Minimal re-renders via React.memo (if needed)