From cbbe06347cdf28f2a667cb37cda539fca9333b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Engebr=C3=A5ten?= Date: Thu, 12 Feb 2026 19:49:27 +0100 Subject: [PATCH] feat(integration): WP-3 workflow filter integration Apply workflow state filter to displayed issues using enrichment data. Unenriched issues treated as 'new' for filtering. Pass enrichments map to IssueList for data flow. Use workflowFilteredIssues instead of raw filteredIssues. Co-Authored-By: Claude Opus 4.6 --- .../src/renderer/components/GitHubIssues.tsx | 12 ++- .../workflow-filter-integration.test.ts | 75 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 apps/frontend/src/renderer/components/github-issues/__tests__/workflow-filter-integration.test.ts diff --git a/apps/frontend/src/renderer/components/GitHubIssues.tsx b/apps/frontend/src/renderer/components/GitHubIssues.tsx index ce78dc75..5734eff1 100644 --- a/apps/frontend/src/renderer/components/GitHubIssues.tsx +++ b/apps/frontend/src/renderer/components/GitHubIssues.tsx @@ -96,6 +96,15 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP const stateCounts = useEnrichmentStore((s) => s.getStateCounts()); const [workflowFilter, setWorkflowFilter] = useState([]); + // Apply workflow filter to issues + const workflowFilteredIssues = useMemo(() => { + if (workflowFilter.length === 0) return filteredIssues; + return filteredIssues.filter((issue) => { + const state = enrichments[String(issue.number)]?.triageState ?? 'new'; + return workflowFilter.includes(state); + }); + }, [filteredIssues, workflowFilter, enrichments]); + const [showInvestigateDialog, setShowInvestigateDialog] = useState(false); const [selectedIssueForInvestigation, setSelectedIssueForInvestigation] = useState(null); @@ -203,7 +212,7 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP {/* Issue List */}
diff --git a/apps/frontend/src/renderer/components/github-issues/__tests__/workflow-filter-integration.test.ts b/apps/frontend/src/renderer/components/github-issues/__tests__/workflow-filter-integration.test.ts new file mode 100644 index 00000000..3e63c8cd --- /dev/null +++ b/apps/frontend/src/renderer/components/github-issues/__tests__/workflow-filter-integration.test.ts @@ -0,0 +1,75 @@ +import { describe, it, expect } from 'vitest'; +import type { IssueEnrichment, WorkflowState } from '../../../../shared/types/enrichment'; + +/** + * Unit tests for the workflow filter logic extracted from GitHubIssues container. + * Tests the pure filtering function without React rendering. + */ + +interface MinimalIssue { + number: number; + state: 'open' | 'closed'; + title: string; +} + +function applyWorkflowFilter( + issues: MinimalIssue[], + enrichments: Record>, + workflowFilter: WorkflowState[], +): MinimalIssue[] { + if (workflowFilter.length === 0) return issues; + return issues.filter((issue) => { + const state = (enrichments[String(issue.number)]?.triageState ?? 'new') as WorkflowState; + return workflowFilter.includes(state); + }); +} + +const issues: MinimalIssue[] = [ + { number: 1, state: 'open', title: 'Issue 1' }, + { number: 2, state: 'open', title: 'Issue 2' }, + { number: 3, state: 'closed', title: 'Issue 3' }, + { number: 4, state: 'open', title: 'Issue 4' }, + { number: 5, state: 'open', title: 'Issue 5' }, +]; + +const enrichments: Record> = { + '1': { triageState: 'new' as WorkflowState }, + '2': { triageState: 'triage' as WorkflowState }, + '3': { triageState: 'done' as WorkflowState }, + '4': { triageState: 'ready' as WorkflowState }, + // Issue 5 has no enrichment — defaults to 'new' +}; + +describe('Workflow filter integration', () => { + it('empty filter shows all issues', () => { + const result = applyWorkflowFilter(issues, enrichments, []); + expect(result.length).toBe(5); + }); + + it('filter to new shows unenriched issues and new state', () => { + const result = applyWorkflowFilter(issues, enrichments, ['new']); + expect(result.map((i) => i.number)).toEqual([1, 5]); + }); + + it('filter to triage and ready shows matching issues', () => { + const result = applyWorkflowFilter(issues, enrichments, ['triage', 'ready']); + expect(result.map((i) => i.number)).toEqual([2, 4]); + }); + + it('filter to done includes closed issues with done state', () => { + const result = applyWorkflowFilter(issues, enrichments, ['done']); + expect(result.map((i) => i.number)).toEqual([3]); + }); + + it('combines with pre-filtered issues (simulating text search)', () => { + // Simulate text search already filtering to issues 1, 2, 3 + const preFiltered = issues.filter((i) => i.number <= 3); + const result = applyWorkflowFilter(preFiltered, enrichments, ['new']); + expect(result.map((i) => i.number)).toEqual([1]); + }); + + it('returns empty when no issues match filter', () => { + const result = applyWorkflowFilter(issues, enrichments, ['in_progress']); + expect(result.length).toBe(0); + }); +});