Refine external object property pills

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-06-23 00:35:05 +00:00
1 parent bb8ad4f0fa
commit d4c4861abd
5 files changed
+47 -14

No files matched your search

@@ -91,7 +91,7 @@ describe("ExternalObjectPill", () => {
expect(html).not.toContain("×");
});
it("uses provider display keys, icons, visible status, and a truncated title when supplied", () => {
it("uses the object link label, provider icon, and visible status when supplied", () => {
const html = renderToStaticMarkup(
<ExternalObjectPill
object={{
@@ -108,9 +108,10 @@ describe("ExternalObjectPill", () => {
}}
/>,
);
expect(html).toContain("Github Pull Request");
expect(html).toContain("Merged");
expect(html).toContain("acme/web#241: Add rich object presentation metadata");
expect(html).toContain("acme/web#241");
expect(html).toContain("text-violet-600");
expect(html).not.toContain("Github Pull Request");
expect(html).toContain('aria-label="GitHub pull request — Merged: acme/web#241: Add rich object presentation metadata"');
});
});
+30 -6
View File
@@ -31,6 +31,32 @@ export interface ExternalObjectPillData {
url?: string | null;
}
function githubObjectLabel(url: string | null | undefined): string | null {
if (!url) return null;
try {
const parsed = new URL(url);
if (parsed.hostname !== "github.com") return null;
const [, owner, repo, kind, number] = parsed.pathname.split("/");
if (!owner || !repo || !number) return null;
if (kind !== "pull" && kind !== "issues") return null;
return `${owner}/${repo}#${number}`;
} catch {
return null;
}
}
function externalObjectValueLabel(object: ExternalObjectPillData, fallback: string): string {
const githubLabel = object.providerKey === "github" ? githubObjectLabel(object.url) : null;
return githubLabel ?? object.displayTitle?.trim() ?? fallback;
}
function externalObjectPillTone(object: ExternalObjectPillData): string {
if (object.statusIconKey === "git-merge") {
return "text-violet-600 border-violet-600 dark:text-violet-400 dark:border-violet-400";
}
return externalObjectStatusIcon[object.statusCategory] ?? externalObjectStatusIconDefault;
}
interface ExternalObjectPillProps {
object: ExternalObjectPillData;
/** Optional external mention count (renders as `×N` superscript when > 1). */
@@ -59,11 +85,12 @@ export function ExternalObjectPill({
children,
inert,
}: ExternalObjectPillProps) {
const tone = externalObjectStatusIcon[object.statusCategory] ?? externalObjectStatusIconDefault;
const tone = externalObjectPillTone(object);
const overlay = externalObjectLivenessOverlay[object.liveness] ?? "";
const providerLabel = externalObjectProviderLabel(object.providerKey);
const typeLabel = externalObjectTypeLabel(object.objectType);
const displayKey = object.displayKey?.trim() || `${providerLabel} ${typeLabel}`;
const valueLabel = externalObjectValueLabel(object, displayKey);
const statusLabel = object.statusLabel ?? externalObjectCategoryLabel(object.statusCategory);
const livenessLabel = externalObjectLivenessLabel(object.liveness);
const ProviderIcon = externalObjectIconForKey(object.iconKey);
@@ -88,8 +115,7 @@ export function ExternalObjectPill({
: object.displayTitle ?? displayKey;
const labelText = children ?? (
<>
<span className="font-medium">{displayKey}</span>
<span className="inline-flex items-center gap-0.5 rounded-full bg-background/70 px-1 text-[10px] font-medium">
<span className="inline-flex items-center gap-0.5 rounded-full border border-current bg-background/70 px-1 text-[10px] font-medium">
<ExternalObjectStatusIcon
category={object.statusCategory}
liveness={object.liveness}
@@ -99,9 +125,7 @@ export function ExternalObjectPill({
/>
<span>{statusLabel}</span>
</span>
{object.displayTitle ? (
<span className="max-w-[16rem] truncate text-muted-foreground/80">{object.displayTitle}</span>
) : null}
<span className="max-w-[16rem] truncate font-medium">{valueLabel}</span>
</>
);
const countSuffix = typeof sourceCount === "number" && sourceCount > 1 ? (
@@ -53,7 +53,9 @@ export function ExternalObjectStatusIcon({
}: ExternalObjectStatusIconProps) {
const reducedMotion = usePrefersReducedMotion();
const Icon = externalObjectIconForKey(statusIconKey) ?? externalObjectIconForCategory(category);
const tone = externalObjectStatusIcon[category] ?? externalObjectStatusIconDefault;
const tone = statusIconKey === "git-merge"
? "text-violet-600 border-violet-600 dark:text-violet-400 dark:border-violet-400"
: externalObjectStatusIcon[category] ?? externalObjectStatusIconDefault;
const livenessSuffix = liveness === "fresh" || liveness === "unknown"
? ""
: ` (${externalObjectLivenessLabel(liveness)})`;
@@ -1855,12 +1855,18 @@ describe("IssueProperties", () => {
expect(container.textContent).toContain("Github Pull Request");
expect(container.textContent).toContain("Github Issue");
expect(container.textContent).toContain("acme/web#241");
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");
expect(label?.querySelector("svg")).toBeTruthy();
const pullRequestLink = Array.from(container.querySelectorAll("a"))
.find((anchor) => anchor.getAttribute("href") === "https://github.com/acme/web/pull/241");
expect(pullRequestLink?.textContent).toContain("acme/web#241");
expect(pullRequestLink?.textContent).toContain("Merged");
expect(pullRequestLink?.textContent).not.toContain("Github Pull Request");
act(() => root.unmount());
});
+4 -4
View File
@@ -211,9 +211,9 @@ function externalObjectRowLabel(group: IssueExternalObjectGroup): React.ReactNod
const displayKey = externalObjectRowDisplayKey(group);
const Icon = externalObjectIconForKey(pill.iconKey);
return (
<span className="inline-flex min-w-0 items-center gap-1">
{Icon ? <Icon aria-hidden="true" className="h-3 w-3 shrink-0" /> : null}
<span className="truncate">{displayKey}</span>
<span className="inline-flex min-w-0 items-start gap-1">
{Icon ? <Icon aria-hidden="true" className="h-3 w-3 shrink-0 mt-0.5" /> : null}
<span className="whitespace-normal break-words leading-tight">{displayKey}</span>
</span>
);
}
@@ -2254,7 +2254,7 @@ export function IssueProperties({
<PropertyRow
key={group.object?.id ?? `${pill.providerKey}:${pill.objectType}:${pill.url ?? "anon"}`}
label={externalObjectRowLabel(externalObject)}
labelClassName="w-32"
labelClassName="w-24 max-w-24 whitespace-normal leading-tight"
>
<ExternalObjectPill
object={pill}