feat: add bulk triage view component and related stories
- Exported BulkTriageViewComponent and its related types from findings module. - Created a new accessibility test suite for score components using axe-core. - Introduced design tokens for score components to standardize styling. - Enhanced score breakdown popover for mobile responsiveness with drag handle. - Added date range selector functionality to score history chart component. - Implemented unit tests for date range selector in score history chart. - Created Storybook stories for bulk triage view and score history chart with date range selector.
This commit is contained in:
246
docs/ui/components/bulk-triage-view.md
Normal file
246
docs/ui/components/bulk-triage-view.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# BulkTriageViewComponent
|
||||
|
||||
Streamlined interface for triaging multiple findings at once with bucket-based organization.
|
||||
|
||||
## Overview
|
||||
|
||||
The `BulkTriageViewComponent` provides a triage-focused view with bucket summary cards, one-click bucket selection, and bulk actions.
|
||||
|
||||
## Selector
|
||||
|
||||
```html
|
||||
<app-bulk-triage-view />
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `findings` | `ScoredFinding[]` | `[]` | Array of scored findings |
|
||||
| `selectedIds` | `Set<string>` | `new Set()` | Currently selected finding IDs |
|
||||
| `processing` | `boolean` | `false` | Whether an action is in progress |
|
||||
|
||||
## Outputs
|
||||
|
||||
| Output | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `selectionChange` | `EventEmitter<string[]>` | Emits when selection changes |
|
||||
| `actionRequest` | `EventEmitter<BulkActionRequest>` | Emits when an action is triggered |
|
||||
| `actionComplete` | `EventEmitter<BulkActionResult>` | Emits when an action completes |
|
||||
|
||||
## Action Types
|
||||
|
||||
```typescript
|
||||
type BulkActionType = 'acknowledge' | 'suppress' | 'assign' | 'escalate';
|
||||
|
||||
interface BulkActionRequest {
|
||||
action: BulkActionType;
|
||||
findingIds: string[];
|
||||
assignee?: string; // For 'assign' action
|
||||
reason?: string; // For 'suppress' action
|
||||
}
|
||||
|
||||
interface BulkActionResult {
|
||||
action: BulkActionType;
|
||||
findingIds: string[];
|
||||
success: boolean;
|
||||
error?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## UI Sections
|
||||
|
||||
### Bucket Summary Cards
|
||||
Four cards showing findings grouped by priority bucket:
|
||||
- **Act Now** (red): 90-100 score
|
||||
- **Schedule Next** (amber): 70-89 score
|
||||
- **Investigate** (blue): 40-69 score
|
||||
- **Watchlist** (gray): 0-39 score
|
||||
|
||||
Each card displays:
|
||||
- Bucket name and color
|
||||
- Finding count
|
||||
- "Select All" button
|
||||
- Selection indicator
|
||||
|
||||
### Action Bar
|
||||
Appears when findings are selected:
|
||||
- Selection count
|
||||
- Clear selection button
|
||||
- Action buttons: Acknowledge, Suppress, Assign, Escalate
|
||||
- Undo button (when history exists)
|
||||
|
||||
### Progress Overlay
|
||||
Shown during bulk operations:
|
||||
- Action name
|
||||
- Progress bar
|
||||
- Percentage complete
|
||||
- Items processed count
|
||||
|
||||
### Modals
|
||||
- **Assign Modal**: Email input for assignee
|
||||
- **Suppress Modal**: Text area for suppression reason
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```html
|
||||
<app-bulk-triage-view
|
||||
[findings]="scoredFindings"
|
||||
[selectedIds]="selectedIds"
|
||||
(selectionChange)="onSelectionChange($event)"
|
||||
(actionRequest)="onActionRequest($event)"
|
||||
/>
|
||||
```
|
||||
|
||||
### Full Implementation
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
selector: 'app-triage-page',
|
||||
template: `
|
||||
<app-bulk-triage-view
|
||||
[findings]="findings()"
|
||||
[selectedIds]="selectedIds()"
|
||||
[processing]="processing()"
|
||||
(selectionChange)="updateSelection($event)"
|
||||
(actionRequest)="handleAction($event)"
|
||||
(actionComplete)="onActionComplete($event)"
|
||||
/>
|
||||
`
|
||||
})
|
||||
export class TriagePageComponent {
|
||||
findings = signal<ScoredFinding[]>([]);
|
||||
selectedIds = signal<Set<string>>(new Set());
|
||||
processing = signal(false);
|
||||
|
||||
private triageService = inject(TriageService);
|
||||
|
||||
updateSelection(ids: string[]): void {
|
||||
this.selectedIds.set(new Set(ids));
|
||||
}
|
||||
|
||||
async handleAction(request: BulkActionRequest): Promise<void> {
|
||||
this.processing.set(true);
|
||||
|
||||
try {
|
||||
switch (request.action) {
|
||||
case 'acknowledge':
|
||||
await this.triageService.acknowledge(request.findingIds);
|
||||
break;
|
||||
case 'suppress':
|
||||
await this.triageService.suppress(request.findingIds, request.reason!);
|
||||
break;
|
||||
case 'assign':
|
||||
await this.triageService.assign(request.findingIds, request.assignee!);
|
||||
break;
|
||||
case 'escalate':
|
||||
await this.triageService.escalate(request.findingIds);
|
||||
break;
|
||||
}
|
||||
|
||||
this.selectedIds.set(new Set());
|
||||
await this.refreshFindings();
|
||||
} finally {
|
||||
this.processing.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Toast Notifications
|
||||
|
||||
```html
|
||||
<app-bulk-triage-view
|
||||
[findings]="findings"
|
||||
[selectedIds]="selectedIds"
|
||||
(actionComplete)="showToast($event)"
|
||||
/>
|
||||
```
|
||||
|
||||
```typescript
|
||||
showToast(result: BulkActionResult): void {
|
||||
if (result.success) {
|
||||
this.toast.success(
|
||||
`${result.action} completed for ${result.findingIds.length} findings`
|
||||
);
|
||||
} else {
|
||||
this.toast.error(`Action failed: ${result.error}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Bucket Selection
|
||||
|
||||
### Select All in Bucket
|
||||
Click "Select All" on a bucket card to select all findings in that bucket.
|
||||
|
||||
### Toggle Bucket
|
||||
Clicking "Select All" when all bucket items are selected will deselect them.
|
||||
|
||||
### Partial Selection
|
||||
When some items in a bucket are selected, the button shows a partial indicator.
|
||||
|
||||
## Action Descriptions
|
||||
|
||||
| Action | Icon | Description |
|
||||
|--------|------|-------------|
|
||||
| Acknowledge | Checkmark | Mark findings as reviewed |
|
||||
| Suppress | Eye-off | Suppress with reason (opens modal) |
|
||||
| Assign | User | Assign to team member (opens modal) |
|
||||
| Escalate | Alert | Mark for urgent attention |
|
||||
|
||||
## Undo Capability
|
||||
|
||||
The component maintains an undo stack for recent actions:
|
||||
- Up to 5 operations stored
|
||||
- Undo restores previous selection
|
||||
- Toast shows "Undo" button after action
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Bucket summary has `aria-label="Findings by priority"`
|
||||
- Select All buttons have `aria-pressed` state
|
||||
- Action bar has `role="toolbar"`
|
||||
- Progress overlay announces to screen readers
|
||||
- Modals trap focus and support Escape to close
|
||||
- Action buttons have descriptive labels
|
||||
|
||||
## Keyboard Navigation
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| Tab | Navigate between elements |
|
||||
| Enter/Space | Activate buttons |
|
||||
| Escape | Close modals |
|
||||
|
||||
## Styling
|
||||
|
||||
```css
|
||||
app-bulk-triage-view {
|
||||
--bucket-card-padding: 16px;
|
||||
--bucket-card-radius: 8px;
|
||||
--action-bar-bg: #f9fafb;
|
||||
--modal-max-width: 400px;
|
||||
}
|
||||
|
||||
/* Bucket colors */
|
||||
.bucket-card.act-now { --bucket-color: #DC2626; }
|
||||
.bucket-card.schedule-next { --bucket-color: #D97706; }
|
||||
.bucket-card.investigate { --bucket-color: #2563EB; }
|
||||
.bucket-card.watchlist { --bucket-color: #6B7280; }
|
||||
```
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
| Breakpoint | Layout |
|
||||
|------------|--------|
|
||||
| > 640px | 4 bucket cards in row |
|
||||
| <= 640px | 2x2 grid, action labels hidden |
|
||||
|
||||
## Related Components
|
||||
|
||||
- [FindingsList](./findings-list.md) - Findings table view
|
||||
- [ScorePill](./score-pill.md) - Score display
|
||||
- [ScoreBadge](./score-badge.md) - Evidence flags
|
||||
Reference in New Issue
Block a user