From 6129188817026c02879284032a91dbb196c56d86 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 21 Apr 2026 07:55:39 -0500 Subject: [PATCH] Fix suggested task interaction card QA issues --- .../IssueThreadInteractionCard.test.tsx | 98 +++++++++++++++++++ .../components/IssueThreadInteractionCard.tsx | 72 ++++++++++---- 2 files changed, 151 insertions(+), 19 deletions(-) create mode 100644 ui/src/components/IssueThreadInteractionCard.test.tsx diff --git a/ui/src/components/IssueThreadInteractionCard.test.tsx b/ui/src/components/IssueThreadInteractionCard.test.tsx new file mode 100644 index 00000000000..16ed40ea66d --- /dev/null +++ b/ui/src/components/IssueThreadInteractionCard.test.tsx @@ -0,0 +1,98 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import type { ComponentProps } from "react"; +import { createRoot, type Root } from "react-dom/client"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { IssueThreadInteractionCard } from "./IssueThreadInteractionCard"; +import { TooltipProvider } from "./ui/tooltip"; +import { + pendingAskUserQuestionsInteraction, + pendingSuggestedTasksInteraction, + rejectedSuggestedTasksInteraction, +} from "../fixtures/issueThreadInteractionFixtures"; + +let root: Root | null = null; +let container: HTMLDivElement | null = null; + +(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + +function renderCard( + props: Partial> = {}, +) { + container = document.createElement("div"); + document.body.appendChild(container); + root = createRoot(container); + + act(() => { + root?.render( + + + , + ); + }); + + return container; +} + +afterEach(() => { + if (root) { + act(() => root?.unmount()); + } + container?.remove(); + root = null; + container = null; +}); + +describe("IssueThreadInteractionCard", () => { + it("exposes pending question options as selectable radio and checkbox controls", () => { + const host = renderCard({ + interaction: pendingAskUserQuestionsInteraction, + onSubmitInteractionAnswers: vi.fn(), + }); + + const singleGroup = host.querySelector('[role="radiogroup"]'); + expect(singleGroup?.getAttribute("aria-labelledby")).toBe( + "interaction-questions-default-collapse-depth-prompt", + ); + + const radios = [...host.querySelectorAll('[role="radio"]')]; + expect(radios).toHaveLength(2); + expect(radios[0]?.getAttribute("aria-checked")).toBe("false"); + + act(() => { + (radios[0] as HTMLButtonElement).click(); + }); + + expect(radios[0]?.getAttribute("aria-checked")).toBe("true"); + expect(radios[1]?.getAttribute("aria-checked")).toBe("false"); + + const multiGroup = host.querySelector('[role="group"]'); + expect(multiGroup?.getAttribute("aria-labelledby")).toBe( + "interaction-questions-default-post-submit-summary-prompt", + ); + expect(host.querySelectorAll('[role="checkbox"]')).toHaveLength(3); + }); + + it("makes child tasks explicit in suggested task trees", () => { + const host = renderCard({ + interaction: pendingSuggestedTasksInteraction, + }); + + expect(host.textContent).toContain("Child task"); + }); + + it("shows an explicit placeholder when a rejected interaction has no reason", () => { + const host = renderCard({ + interaction: { + ...rejectedSuggestedTasksInteraction, + result: { version: 1 }, + }, + }); + + expect(host.textContent).toContain("No reason provided."); + }); +}); diff --git a/ui/src/components/IssueThreadInteractionCard.tsx b/ui/src/components/IssueThreadInteractionCard.tsx index 7880a967daf..d95314be82e 100644 --- a/ui/src/components/IssueThreadInteractionCard.tsx +++ b/ui/src/components/IssueThreadInteractionCard.tsx @@ -98,23 +98,23 @@ function statusClasses(status: IssueThreadInteraction["status"]) { case "accepted": case "answered": return { - shell: "border-emerald-300/70 bg-[linear-gradient(145deg,rgba(16,185,129,0.14),rgba(255,255,255,0.96)_42%,rgba(6,182,212,0.08))]", + shell: "border-emerald-300/80 bg-card", badge: "border-emerald-400/60 bg-emerald-100/80 text-emerald-950", }; case "rejected": return { - shell: "border-rose-300/70 bg-[linear-gradient(145deg,rgba(244,63,94,0.12),rgba(255,255,255,0.96)_42%,rgba(251,191,36,0.08))]", + shell: "border-rose-300/80 bg-card", badge: "border-rose-400/60 bg-rose-100/85 text-rose-950", }; case "failed": case "expired": return { - shell: "border-amber-300/70 bg-[linear-gradient(145deg,rgba(245,158,11,0.14),rgba(255,255,255,0.96)_42%,rgba(148,163,184,0.08))]", + shell: "border-amber-300/80 bg-card", badge: "border-amber-400/60 bg-amber-100/85 text-amber-950", }; default: return { - shell: "border-sky-300/70 bg-[linear-gradient(145deg,rgba(14,165,233,0.10),rgba(255,255,255,0.96)_42%,rgba(250,204,21,0.08))]", + shell: "border-sky-300/80 bg-card", badge: "border-sky-400/60 bg-sky-100/85 text-sky-950", }; } @@ -134,7 +134,7 @@ function TaskField({ className={cn( "inline-flex items-center rounded-full border px-2 py-0.5 text-[10px] font-medium uppercase tracking-[0.16em]", tone === "default" - ? "border-border/70 bg-background/80 text-foreground/75" + ? "border-border/70 bg-background/80 text-foreground" : "border-border/60 bg-background/60 text-muted-foreground", )} > @@ -193,11 +193,17 @@ function TaskTreeNode({ ); return ( -
+
0 && "relative pl-5")}> + {depth > 0 ? ( +