mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 09:07:46 +02:00
Fix covered blocked status icons
This commit is contained in:
1 parent
0ee91c1651
commit
f626996e63
5 files changed
+63
-3
No files matched your search
@@ -83,7 +83,9 @@ vi.mock("../lib/assignees", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("./StatusIcon", () => ({
|
||||
StatusIcon: ({ status }: { status: string }) => <span>{status}</span>,
|
||||
StatusIcon: ({ status, blockerAttention }: { status: string; blockerAttention?: Issue["blockerAttention"] }) => (
|
||||
<span data-status-icon-state={blockerAttention?.state}>{status}</span>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./PriorityIcon", () => ({
|
||||
@@ -392,6 +394,29 @@ describe("IssueProperties", () => {
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
it("passes blocker attention to the sidebar status icon", async () => {
|
||||
const root = renderProperties(container, {
|
||||
issue: createIssue({
|
||||
status: "blocked",
|
||||
blockerAttention: {
|
||||
state: "covered",
|
||||
reason: "active_child",
|
||||
unresolvedBlockerCount: 1,
|
||||
coveredBlockerCount: 1,
|
||||
attentionBlockerCount: 0,
|
||||
sampleBlockerIdentifier: "PAP-2",
|
||||
},
|
||||
}),
|
||||
childIssues: [],
|
||||
onUpdate: vi.fn(),
|
||||
});
|
||||
await flush();
|
||||
|
||||
expect(container.querySelector('[data-status-icon-state="covered"]')?.textContent).toBe("blocked");
|
||||
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
it("renders blocked-by issues as direct chips and edits them from an add action", async () => {
|
||||
const onUpdate = vi.fn();
|
||||
mockIssuesApi.list.mockResolvedValue([
|
||||
|
||||
@@ -1044,6 +1044,7 @@ export function IssueProperties({
|
||||
<PropertyRow label="Status">
|
||||
<StatusIcon
|
||||
status={issue.status}
|
||||
blockerAttention={issue.blockerAttention}
|
||||
onChange={(status) => onUpdate({ status })}
|
||||
showLabel
|
||||
/>
|
||||
|
||||
@@ -12,6 +12,7 @@ export type IssueDetailHeaderSeed = {
|
||||
identifier: string | null;
|
||||
title: string;
|
||||
status: Issue["status"];
|
||||
blockerAttention?: Issue["blockerAttention"];
|
||||
priority: Issue["priority"];
|
||||
projectId: string | null;
|
||||
projectName: string | null;
|
||||
@@ -47,11 +48,15 @@ function isIssueDetailHeaderSeed(value: unknown): value is IssueDetailHeaderSeed
|
||||
candidate.originKind === undefined || typeof candidate.originKind === "string";
|
||||
const hasOriginId =
|
||||
candidate.originId === undefined || candidate.originId === null || typeof candidate.originId === "string";
|
||||
const hasBlockerAttention =
|
||||
candidate.blockerAttention === undefined
|
||||
|| (typeof candidate.blockerAttention === "object" && candidate.blockerAttention !== null);
|
||||
return (
|
||||
typeof candidate.id === "string"
|
||||
&& (candidate.identifier === null || typeof candidate.identifier === "string")
|
||||
&& typeof candidate.title === "string"
|
||||
&& typeof candidate.status === "string"
|
||||
&& hasBlockerAttention
|
||||
&& typeof candidate.priority === "string"
|
||||
&& (candidate.projectId === null || typeof candidate.projectId === "string")
|
||||
&& (candidate.projectName === null || typeof candidate.projectName === "string")
|
||||
@@ -66,6 +71,7 @@ function createIssueDetailHeaderSeed(issue: Issue): IssueDetailHeaderSeed {
|
||||
identifier: issue.identifier ?? null,
|
||||
title: issue.title,
|
||||
status: issue.status,
|
||||
blockerAttention: issue.blockerAttention,
|
||||
priority: issue.priority,
|
||||
projectId: issue.projectId ?? null,
|
||||
projectName: issue.project?.name ?? null,
|
||||
|
||||
@@ -229,7 +229,9 @@ vi.mock("../components/ScrollToBottom", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("../components/StatusIcon", () => ({
|
||||
StatusIcon: ({ status }: { status: string }) => <span>{status}</span>,
|
||||
StatusIcon: ({ status, blockerAttention }: { status: string; blockerAttention?: Issue["blockerAttention"] }) => (
|
||||
<span data-status-icon-state={blockerAttention?.state}>{status}</span>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("../components/PriorityIcon", () => ({
|
||||
@@ -814,6 +816,31 @@ describe("IssueDetail", () => {
|
||||
expect(consoleErrorSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("passes blocker attention to the issue detail header status icon", async () => {
|
||||
mockIssuesApi.get.mockResolvedValue(createIssue({
|
||||
status: "blocked",
|
||||
blockerAttention: {
|
||||
state: "covered",
|
||||
reason: "active_child",
|
||||
unresolvedBlockerCount: 1,
|
||||
coveredBlockerCount: 1,
|
||||
attentionBlockerCount: 0,
|
||||
sampleBlockerIdentifier: "PAP-2",
|
||||
},
|
||||
}));
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<IssueDetail />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
await flushReact();
|
||||
|
||||
expect(container.querySelector('[data-status-icon-state="covered"]')?.textContent).toBe("blocked");
|
||||
});
|
||||
|
||||
it("refreshes subtree pause state after resuming a hold", async () => {
|
||||
const childIssue = createIssue({
|
||||
id: "child-1",
|
||||
|
||||
@@ -382,7 +382,7 @@ function IssueDetailLoadingState({
|
||||
<div className="flex items-center gap-2 min-w-0 flex-wrap">
|
||||
{headerSeed ? (
|
||||
<>
|
||||
<StatusIcon status={headerSeed.status} />
|
||||
<StatusIcon status={headerSeed.status} blockerAttention={headerSeed.blockerAttention} />
|
||||
<PriorityIcon priority={headerSeed.priority} />
|
||||
{identifier ? (
|
||||
<span className="text-sm font-mono text-muted-foreground shrink-0">{identifier}</span>
|
||||
@@ -2853,6 +2853,7 @@ export function IssueDetail() {
|
||||
<div className="flex items-center gap-2 min-w-0 flex-wrap">
|
||||
<StatusIcon
|
||||
status={issue.status}
|
||||
blockerAttention={issue.blockerAttention}
|
||||
onChange={(status) => updateIssue.mutate({ status })}
|
||||
/>
|
||||
<PriorityIcon
|
||||
|
||||
Reference in new issue
Block a user