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:
StellaOps Bot
2025-12-26 01:01:35 +02:00
parent ed3079543c
commit 17613acf57
45 changed files with 9418 additions and 64 deletions

View File

@@ -0,0 +1,260 @@
# FindingsListComponent
Comprehensive findings list with Evidence-Weighted Score (EWS) integration, filtering, and bulk selection.
## Overview
The `FindingsListComponent` displays a sortable, filterable table of vulnerability findings with integrated score loading and display.
## Selector
```html
<app-findings-list />
```
## Inputs
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `findings` | `Finding[]` | `[]` | Array of findings to display |
| `autoLoadScores` | `boolean` | `true` | Auto-fetch scores when findings change |
## Outputs
| Output | Type | Description |
|--------|------|-------------|
| `findingSelect` | `EventEmitter<ScoredFinding>` | Emits when a finding row is clicked |
| `selectionChange` | `EventEmitter<string[]>` | Emits finding IDs when selection changes |
## Data Structures
### Finding
```typescript
interface Finding {
id: string; // Unique finding ID
advisoryId: string; // CVE/GHSA ID
packageName: string;
packageVersion: string;
severity: 'critical' | 'high' | 'medium' | 'low';
status: 'open' | 'in_progress' | 'fixed' | 'excepted';
publishedAt?: string; // ISO 8601
}
```
### ScoredFinding
```typescript
interface ScoredFinding extends Finding {
score?: EvidenceWeightedScoreResult;
scoreLoading: boolean;
}
```
## Table Columns
| Column | Description | Sortable |
|--------|-------------|----------|
| Checkbox | Bulk selection | No |
| Score | EWS score pill with flags | Yes |
| Advisory | CVE/GHSA identifier | Yes |
| Package | Package name and version | Yes |
| Severity | CVSS severity level | Yes |
| Status | Finding status | Yes |
## Features
### Score Loading
When `autoLoadScores` is true, scores are fetched automatically via the `SCORING_API` injection token.
### Bucket Filtering
Filter findings by priority bucket using the chip filters:
- All (default)
- Act Now (90-100)
- Schedule Next (70-89)
- Investigate (40-69)
- Watchlist (0-39)
### Flag Filtering
Filter by active score flags:
- Live Signal
- Proven Path
- Vendor N/A
- Speculative
### Search
Text search across advisory ID and package name.
### Sorting
Click column headers to sort. Click again to reverse order.
### Bulk Selection
- Click checkboxes to select individual findings
- Use "Select All" to select visible findings
- Selection persists across filter changes
## Usage Examples
### Basic Usage
```html
<app-findings-list
[findings]="findings"
(findingSelect)="openFinding($event)"
/>
```
### Without Auto-Loading Scores
```html
<app-findings-list
[findings]="findings"
[autoLoadScores]="false"
/>
```
### With Selection Handling
```html
<app-findings-list
[findings]="findings"
[autoLoadScores]="true"
(selectionChange)="onSelectionChange($event)"
/>
<div class="bulk-actions" *ngIf="selectedIds.length > 0">
<button (click)="acknowledgeSelected()">
Acknowledge ({{ selectedIds.length }})
</button>
</div>
```
```typescript
selectedIds: string[] = [];
onSelectionChange(ids: string[]): void {
this.selectedIds = ids;
}
```
### Full Feature Example
```typescript
@Component({
selector: 'app-vulnerability-dashboard',
template: `
<div class="dashboard">
<h1>Vulnerabilities</h1>
<app-findings-list
[findings]="findings()"
[autoLoadScores]="true"
(findingSelect)="openFindingDetail($event)"
(selectionChange)="updateSelection($event)"
/>
@if (selectedFinding()) {
<app-finding-detail-panel
[finding]="selectedFinding()"
(close)="closeFindingDetail()"
/>
}
</div>
`
})
export class VulnerabilityDashboardComponent {
findings = signal<Finding[]>([]);
selectedFinding = signal<ScoredFinding | null>(null);
selectedIds = signal<string[]>([]);
constructor(private findingsService: FindingsService) {
this.loadFindings();
}
async loadFindings(): Promise<void> {
this.findings.set(await this.findingsService.getFindings());
}
openFindingDetail(finding: ScoredFinding): void {
this.selectedFinding.set(finding);
}
closeFindingDetail(): void {
this.selectedFinding.set(null);
}
updateSelection(ids: string[]): void {
this.selectedIds.set(ids);
}
}
```
## Dependency Injection
The component requires a `SCORING_API` provider:
```typescript
import { SCORING_API, ScoringApiService } from '@app/core/services/scoring.service';
@NgModule({
providers: [
{ provide: SCORING_API, useClass: ScoringApiService }
]
})
export class AppModule {}
```
### Mock API for Testing
```typescript
import { MockScoringApi } from '@app/core/services/scoring.service';
TestBed.configureTestingModule({
providers: [
{ provide: SCORING_API, useClass: MockScoringApi }
]
});
```
## Empty States
### No Findings
```html
<!-- Displays: "No findings to display" -->
<app-findings-list [findings]="[]" />
```
### No Matches
When filters result in no matches:
```html
<!-- Displays: "No findings match the current filters" -->
```
## Accessibility
- Proper table semantics with `<table>`, `<thead>`, `<tbody>`
- Sortable columns use `aria-sort`
- Checkboxes have accessible labels
- Filter chips are keyboard navigable
- Focus management on filter/sort changes
- Screen reader announces result counts
## Styling
```css
app-findings-list {
--table-header-bg: #f9fafb;
--table-border-color: #e5e7eb;
--table-row-hover: #f3f4f6;
--table-row-selected: #eff6ff;
}
```
## Related Components
- [ScorePill](./score-pill.md) - Score display in table
- [ScoreBadge](./score-badge.md) - Flag badges in table
- [ScoreBreakdownPopover](./score-breakdown-popover.md) - Score details on click
- [BulkTriageView](./bulk-triage-view.md) - Bulk operations