From f6ba07fe1c080963d29f7959ace5f6e97dc15c5a Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 22 Jun 2026 23:06:07 +0000 Subject: [PATCH] Render each external object as a property row Co-Authored-By: Paperclip --- ui/src/components/IssueProperties.test.tsx | 26 +++++++++- ui/src/components/IssueProperties.tsx | 55 ++++++++++++---------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/ui/src/components/IssueProperties.test.tsx b/ui/src/components/IssueProperties.test.tsx index c92e5785a33..6e5de76ecfe 100644 --- a/ui/src/components/IssueProperties.test.tsx +++ b/ui/src/components/IssueProperties.test.tsx @@ -1798,7 +1798,7 @@ describe("IssueProperties", () => { act(() => root.unmount()); }); - it("uses external object display metadata for the properties row label", async () => { + it("renders each external object as its own properties row using display metadata", async () => { const root = renderProperties(container, { issue: createIssue(), childIssues: [], @@ -1827,12 +1827,36 @@ describe("IssueProperties", () => { sourceLabels: ["Description"], }, }, + { + mentionCount: 1, + sourceLabels: ["Comment"], + pill: { + providerKey: "github", + objectType: "issue", + displayKey: "Github Issue", + iconKey: "github", + statusCategory: "open", + statusIconKey: "circle-dot", + statusLabel: "Open", + liveness: "fresh", + displayTitle: "acme/web#12: Follow-up", + url: "https://github.com/acme/web/issues/12", + }, + group: { + object: null, + mentions: [], + mentionCount: 1, + sourceLabels: ["Comment"], + }, + }, ], }); await flush(); expect(container.textContent).toContain("Github Pull Request"); + expect(container.textContent).toContain("Github Issue"); expect(container.textContent).toContain("Merged"); + expect(container.textContent).toContain("Open"); expect(container.textContent).not.toContain("External objects"); const label = Array.from(container.querySelectorAll("span")) .find((span) => span.textContent === "Github Pull Request"); diff --git a/ui/src/components/IssueProperties.tsx b/ui/src/components/IssueProperties.tsx index 4843c38778c..5c268b82300 100644 --- a/ui/src/components/IssueProperties.tsx +++ b/ui/src/components/IssueProperties.tsx @@ -37,7 +37,12 @@ import { IssueReferencePill } from "./IssueReferencePill"; import { formatDate, formatDateTime, cn, projectUrl } from "../lib/utils"; import { ExternalObjectPill } from "./ExternalObjectPill"; import type { IssueExternalObjectGroup } from "../hooks/useIssueExternalObjects"; -import { externalObjectIconForKey, externalObjectToneSeverity } from "../lib/external-objects"; +import { + externalObjectIconForKey, + externalObjectProviderLabel, + externalObjectToneSeverity, + externalObjectTypeLabel, +} from "../lib/external-objects"; import { timeAgo } from "../lib/timeAgo"; import { Button } from "@/components/ui/button"; import { ToggleSwitch } from "@/components/ui/toggle-switch"; @@ -190,19 +195,11 @@ function sortExternalObjectGroups(groups: IssueExternalObjectGroup[]) { }); } -function externalObjectRowLabel(groups: IssueExternalObjectGroup[] | undefined): React.ReactNode { - if (!groups || groups.length === 0) return "External objects"; - const sorted = sortExternalObjectGroups(groups); - const primary = sorted[0]?.pill; - const displayKey = primary?.displayKey?.trim(); - if (!primary || !displayKey) return "External objects"; - const iconKey = primary.iconKey ?? null; - const allSameDisplay = sorted.every(({ pill }) => - (pill.displayKey?.trim() ?? "") === displayKey && (pill.iconKey ?? null) === iconKey, - ); - if (!allSameDisplay) return "External objects"; - - const Icon = externalObjectIconForKey(iconKey); +function externalObjectRowLabel(group: IssueExternalObjectGroup): React.ReactNode { + const { pill } = group; + const displayKey = pill.displayKey?.trim() + || `${externalObjectProviderLabel(pill.providerKey)} ${externalObjectTypeLabel(pill.objectType)}`; + const Icon = externalObjectIconForKey(pill.iconKey); return ( {Icon ?