From 988409dfc530930f77d241a83aaa420a7e8819c9 Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 15 Apr 2026 10:05:23 -0500 Subject: [PATCH] fix: address PR 3744 UI follow-ups --- ui/src/components/IssueProperties.test.tsx | 3 ++ ui/src/components/IssueProperties.tsx | 17 ++++---- ui/src/components/IssuesList.test.tsx | 41 +++++++++++++++++++ ui/src/components/IssuesList.tsx | 9 +++- .../transcript/RunTranscriptView.test.tsx | 8 ++-- 5 files changed, 65 insertions(+), 13 deletions(-) diff --git a/ui/src/components/IssueProperties.test.tsx b/ui/src/components/IssueProperties.test.tsx index 3c92683a7b0..2069d76b1bf 100644 --- a/ui/src/components/IssueProperties.test.tsx +++ b/ui/src/components/IssueProperties.test.tsx @@ -323,6 +323,9 @@ describe("IssueProperties", () => { const selectedParentTrigger = Array.from(container.querySelectorAll("button")) .find((button) => button.textContent?.includes("PAP-2 Candidate parent")); expect(selectedParentTrigger).not.toBeUndefined(); + const parentLink = container.querySelector('a[href="/issues/PAP-2"]'); + expect(parentLink).not.toBeNull(); + expect(selectedParentTrigger!.contains(parentLink)).toBe(false); await act(async () => { selectedParentTrigger!.dispatchEvent(new MouseEvent("click", { bubbles: true })); diff --git a/ui/src/components/IssueProperties.tsx b/ui/src/components/IssueProperties.tsx index 32592a885fd..0deebe6376d 100644 --- a/ui/src/components/IssueProperties.tsx +++ b/ui/src/components/IssueProperties.tsx @@ -708,17 +708,19 @@ export function IssueProperties({ {parentIdentifier ? `${parentIdentifier} ` : ""} {parentTitle} - e.stopPropagation()} - > - - ) : ( No parent ); + const parentLink = issue.parentId ? ( + e.stopPropagation()} + > + + + ) : undefined; const parentOptions = (allIssues ?? []) .filter((candidate) => candidate.id !== issue.id) .filter((candidate) => !descendantIssueIds.has(candidate.id)) @@ -944,6 +946,7 @@ export function IssueProperties({ triggerContent={parentTrigger} triggerClassName="min-w-0 max-w-full" popoverClassName="w-72" + extra={parentLink} > {parentContent} diff --git a/ui/src/components/IssuesList.test.tsx b/ui/src/components/IssuesList.test.tsx index 9dee6d6b91b..4c263f96ed5 100644 --- a/ui/src/components/IssuesList.test.tsx +++ b/ui/src/components/IssuesList.test.tsx @@ -421,6 +421,47 @@ describe("IssuesList", () => { }); }); + it("skips deferred row sizing for expanded parent rows with visible children", async () => { + const parentIssue = createIssue({ + id: "issue-parent", + identifier: "PAP-1", + title: "Parent issue", + }); + const childIssue = createIssue({ + id: "issue-child", + identifier: "PAP-2", + title: "Child issue", + parentId: "issue-parent", + }); + + const { root } = renderWithQueryClient( + undefined} + />, + container, + ); + + await waitForAssertion(() => { + const rows = Array.from(container.querySelectorAll('[data-testid="issue-row"]')); + const parentRow = rows.find((row) => row.textContent?.includes("Parent issue")); + const childRow = rows.find((row) => row.textContent?.includes("Child issue")); + expect(parentRow).not.toBeUndefined(); + expect(childRow).not.toBeUndefined(); + expect((parentRow?.parentElement as HTMLDivElement | null)?.style.contentVisibility).toBe(""); + expect((parentRow?.parentElement as HTMLDivElement | null)?.style.containIntrinsicSize).toBe(""); + expect((childRow?.parentElement as HTMLDivElement | null)?.style.contentVisibility).toBe("auto"); + expect((childRow?.parentElement as HTMLDivElement | null)?.style.containIntrinsicSize).toBe("44px"); + }); + + act(() => { + root.unmount(); + }); + }); + it("uses context-scoped persisted column visibility", async () => { localStorage.setItem("paperclip:test-issues:company-1:issue-columns", JSON.stringify(["id", "assignee"])); diff --git a/ui/src/components/IssuesList.tsx b/ui/src/components/IssuesList.tsx index 5b8a562a28c..1f8ebcdcba7 100644 --- a/ui/src/components/IssuesList.tsx +++ b/ui/src/components/IssuesList.tsx @@ -835,6 +835,7 @@ export function IssuesList({ const hasChildren = children.length > 0; const totalDescendants = hasChildren ? countDescendants(issue.id, childMap) : 0; const isExpanded = !viewState.collapsedParents.includes(issue.id); + const useDeferredRowRendering = !(hasChildren && isExpanded); const issueProject = issue.projectId ? projectById.get(issue.projectId) ?? null : null; const parentIssue = issue.parentId ? issueById.get(issue.parentId) ?? null : null; const toggleCollapse = (e: { preventDefault: () => void; stopPropagation: () => void }) => { @@ -852,8 +853,12 @@ export function IssuesList({ key={issue.id} style={{ ...(depth > 0 ? { paddingLeft: `${depth * 16}px` } : {}), - contentVisibility: "auto", - containIntrinsicSize: "44px", + ...(useDeferredRowRendering + ? { + contentVisibility: "auto", + containIntrinsicSize: "44px", + } + : {}), }} > { ); expect(html).toContain("world"); - expect(html).toContain("
  • first
  • "); - expect(html).toContain("
  • second
  • "); + expect(html).toMatch(/]*>first<\/li>/); + expect(html).toMatch(/]*>second<\/li>/); }); it("hides saved-session resume skip stderr from nice mode normalization", () => { @@ -106,8 +106,8 @@ describe("RunTranscriptView", () => { ); expect(html).toContain("

    Summary

    "); - expect(html).toContain("
  • fixed deploy config
  • "); - expect(html).toContain("
  • posted issue update
  • "); + expect(html).toMatch(/]*>fixed deploy config<\/li>/); + expect(html).toMatch(/]*>posted issue update<\/li>/); expect(html).not.toContain("result"); }); });