diff --git a/packages/views/issues/components/issue-detail.test.tsx b/packages/views/issues/components/issue-detail.test.tsx index 5f87a33e69..38d4c2b450 100644 --- a/packages/views/issues/components/issue-detail.test.tsx +++ b/packages/views/issues/components/issue-detail.test.tsx @@ -844,6 +844,26 @@ describe("IssueDetail (shared)", () => { expect(screen.getByText(/changed priority/i)).toBeInTheDocument(); }); + it("renders activity rows with unknown status values without crashing", async () => { + mockApiObj.listTimeline.mockResolvedValue([ + { + type: "activity", + id: "act-unknown-status", + actor_type: "member", + actor_id: "user-1", + action: "status_changed", + details: { from: "todo", to: "mystery_status" }, + created_at: "2026-01-18T00:00:00Z", + }, + ] as TimelineEntry[]); + + renderIssueDetail(); + + await waitFor(() => { + expect(screen.getByText(/from Todo to mystery_status/i)).toBeInTheDocument(); + }); + }); + it("truncates the trailing activity block to the most recent 8 entries with a show-more toggle", async () => { // 10 activities, all in the trailing block (no comment after them, so it's // the trailing block by definition). Alternating action types so the diff --git a/packages/views/issues/components/priority-icon.tsx b/packages/views/issues/components/priority-icon.tsx index e24c1666f3..63774a6bab 100644 --- a/packages/views/issues/components/priority-icon.tsx +++ b/packages/views/issues/components/priority-icon.tsx @@ -6,14 +6,14 @@ export function PriorityIcon({ className = "", inheritColor = false, }: { - priority: IssuePriority; + priority: IssuePriority | string; className?: string; inheritColor?: boolean; }) { - const cfg = PRIORITY_CONFIG[priority]; + const cfg = priority in PRIORITY_CONFIG ? PRIORITY_CONFIG[priority as IssuePriority] : null; // "none" — simple horizontal dashes - if (cfg.bars === 0) { + if (!cfg || cfg.bars === 0) { return ( { + it("renders a muted fallback for unknown status values", () => { + const { container } = render(); + + const icon = container.querySelector("svg"); + expect(icon).toHaveClass("text-muted-foreground"); + }); + + it("renders a muted fallback for unknown priority values", () => { + const { container } = render(); + + const icon = container.querySelector("svg"); + expect(icon).toHaveClass("text-muted-foreground"); + }); +}); diff --git a/packages/views/issues/components/status-icon.tsx b/packages/views/issues/components/status-icon.tsx index 2bd52d2592..81d6b8032c 100644 --- a/packages/views/issues/components/status-icon.tsx +++ b/packages/views/issues/components/status-icon.tsx @@ -162,18 +162,19 @@ export function StatusIcon({ className = "h-4 w-4", inheritColor = false, }: { - status: IssueStatus; + status: IssueStatus | string; className?: string; inheritColor?: boolean; }) { - const cfg = STATUS_CONFIG[status]; - const Renderer = STATUS_RENDERERS[status]; + const knownStatus = status in STATUS_RENDERERS ? (status as IssueStatus) : null; + const cfg = knownStatus ? STATUS_CONFIG[knownStatus] : null; + const Renderer = knownStatus ? STATUS_RENDERERS[knownStatus] : TodoIcon; return (