fix(web): open each linked PR in its own tab from the "+" menu (#1636)
This commit is contained in:
@@ -5,6 +5,7 @@ import { IconGitPullRequest } from "@tabler/icons-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { PRCIPopover } from "@/components/github/pr-ci-popover";
|
||||
import { getPRStatusColor, pickDefaultPR } from "@/components/github/pr-task-icon";
|
||||
import { prIdentitySlug } from "@/components/github/pr-utils";
|
||||
import { usePRFeedbackBackgroundSync } from "@/hooks/domains/github/use-pr-ci-popover";
|
||||
import type { TaskPR } from "@/lib/types/github";
|
||||
|
||||
@@ -18,8 +19,16 @@ function PRFeedbackWarmer({ pr }: { pr: TaskPR }) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOM id for a PR tab's roving-tabindex button.
|
||||
*
|
||||
* @param uid - The popover instance's `useId()` value, so ids stay unique
|
||||
* across multiple popovers mounted at once.
|
||||
* @param pr - The PR the tab renders; identity-scoped so two PRs never
|
||||
* collide within one popover.
|
||||
*/
|
||||
function prTabDomId(uid: string, pr: TaskPR): string {
|
||||
return `${uid}-pr-tab-${pr.repo}-${pr.pr_number}`;
|
||||
return `${uid}-pr-tab-${prIdentitySlug(pr)}`;
|
||||
}
|
||||
|
||||
function PRTab({
|
||||
@@ -45,7 +54,7 @@ function PRTab({
|
||||
tabIndex={active ? 0 : -1}
|
||||
aria-selected={active}
|
||||
aria-controls={panelId}
|
||||
data-testid={`pr-popover-tab-${pr.repo}-${pr.pr_number}`}
|
||||
data-testid={`pr-popover-tab-${prIdentitySlug(pr)}`}
|
||||
data-active={active ? "true" : "false"}
|
||||
onClick={() => onSelect(pr)}
|
||||
className={cn(
|
||||
|
||||
@@ -624,10 +624,10 @@ describe("PRStatusChip — multiple PRs", () => {
|
||||
});
|
||||
expect(document.querySelector(DRAWER_SELECTOR)).not.toBeNull();
|
||||
expect(document.querySelector("[data-testid='pr-multi-popover']")).not.toBeNull();
|
||||
// One tab per PR (testid is repo-scoped so same-number PRs on
|
||||
// different repos stay unique).
|
||||
expect(document.querySelector("[data-testid='pr-popover-tab-demo-1']")).not.toBeNull();
|
||||
expect(document.querySelector("[data-testid='pr-popover-tab-demo-2']")).not.toBeNull();
|
||||
// One tab per PR (testid is owner+repo-scoped so same-number PRs on
|
||||
// different repos, or repos sharing a name across owners, stay unique).
|
||||
expect(document.querySelector("[data-testid='pr-popover-tab-acme-demo-1']")).not.toBeNull();
|
||||
expect(document.querySelector("[data-testid='pr-popover-tab-acme-demo-2']")).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
isPRWaitingOnBranchProtection,
|
||||
} from "@/components/github/pr-task-icon";
|
||||
import { prTaskKey } from "@/components/github/pr-detail-panel";
|
||||
import { prIdentitySlug } from "@/components/github/pr-utils";
|
||||
import { PR_CI_DESKTOP_POPOVER_SCROLL_CLASS, PRCIPopover } from "@/components/github/pr-ci-popover";
|
||||
import { MultiPRCIPopover } from "@/components/github/multi-pr-ci-popover";
|
||||
import { useAppStore } from "@/components/state-provider";
|
||||
@@ -307,7 +308,7 @@ function MultiPRMenuContent({ prs }: { prs: TaskPR[] }) {
|
||||
key={pr.id}
|
||||
onClick={() => addPRPanel(prTaskKey(pr), activeSessionId)}
|
||||
className="cursor-pointer gap-2"
|
||||
data-testid={`pr-topbar-menu-item-${pr.pr_number}`}
|
||||
data-testid={`pr-topbar-menu-item-${prIdentitySlug(pr)}`}
|
||||
>
|
||||
<IconGitPullRequest className={`h-4 w-4 shrink-0 ${getPRStatusColor(pr)}`} />
|
||||
<div className="flex flex-col min-w-0 flex-1">
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
import type { TaskPR } from "@/lib/types/github";
|
||||
|
||||
/** Shared label for the PR dockview panel tab and dropdown items. */
|
||||
export function prPanelLabel(prNumber: number): string {
|
||||
return `PR #${prNumber}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stable per-PR identifier for React keys, DOM ids, and e2e test ids.
|
||||
* Includes owner + repo + PR number so two repos that share a name (or two
|
||||
* owners that share a repo name) never collide on PR number alone.
|
||||
*/
|
||||
export function prIdentitySlug(pr: TaskPR): string {
|
||||
return `${pr.owner}-${pr.repo}-${pr.pr_number}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user