authorgravatar for 97427849+flamboh@users.noreply.github.comoliver <97427849+flamboh@users.noreply.github.com> 2026-09-29 17:40:55-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-29 21:40:55-03:00
log0fcd5f90611451cca842689faea53b5450c022da
tree25019ae99f68876b1e84f10b3c11f9f04ee9ec27
parent2a23c30ea60854cb17bec1a9d248032207c5d6b2
signature Signed by PGP key B5690EEEBB952194

fix(web): multi-PR badges open the linked pull requests panel (#13211)


4 files changed, 19 insertions(+), 17 deletions(-)

apps/web/src/components/BranchToolbarBranchSelector.tsx+1-1
......@@ -793,7 +793,7 @@ export function BranchToolbarBranchSelector({
793793 number={prNumber}
794794 url={prUrl}
795795 status={displayedPrStatus}
796 onOpenStack={() => useRightPanelStore.getState().open(threadRef, "pull-requests")}
796 onOpenList={() => useRightPanelStore.getState().open(threadRef, "pull-requests")}
797797 onOpenPullRequest={(event) => {
798798 if (prUrl) openPrLink(event, prUrl);
799799 }}
apps/web/src/components/Sidebar.tsx+4-3
......@@ -1515,11 +1515,12 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
15151515 const accessibleTitle = isRenaming ? null : <span className="sr-only">{thread.title}</span>;
15161516
15171517 // Stacks show their layer count; multiple unrelated links show their total count.
1518 // Plain clicks open T3; individual PR links also support opening the host in a new tab.
1518 // Either opens the thread's pull requests tab; a single PR link opens that PR and still
1519 // supports opening the host in a new tab.
15191520 const prBadgeShape = supportsMultiplePullRequests
15201521 ? resolveThreadPullRequestBadge(thread.pullRequests)
15211522 : null;
1522 const handlePrStackClick = useCallback(() => {
1523 const handlePrListClick = useCallback(() => {
15231524 useRightPanelStore.getState().open(threadRef, "pull-requests");
15241525 if (!props.isActive) onThreadActivate(threadRef);
15251526 }, [onThreadActivate, props.isActive, threadRef]);
......@@ -1531,7 +1532,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
15311532 number={pr?.number ?? currentLinkedPr?.number}
15321533 url={pr?.url ?? currentLinkedPr?.url}
15331534 status={prStatus}
1534 onOpenStack={handlePrStackClick}
1535 onOpenList={handlePrListClick}
15351536 onOpenPullRequest={handlePrClick}
15361537 />
15371538 ) : null;
apps/web/src/components/ThreadStatusIndicators.tsx+13-12
......@@ -195,7 +195,8 @@ export function resolveThreadPullRequestBadgePresentation({
195195 * The linked-PR badge shared by the sidebar and composer footer. The badge owns what it shows:
196196 * the state glyph and number at the meta size, in the state's color. The caller owns the control
197197 * it sits in through `render` (an inline link in a sidebar row, a toolbar control in the
198 * composer), and the badge fills in the link or stack button behavior.
198 * composer), and the badge fills in the behavior: a single PR is a link to it, while a stack or
199 * several linked PRs is a button that opens the thread's pull requests tab.
199200 */
200201export function ThreadPullRequestBadgeControl({
201202 render,
......@@ -203,7 +204,7 @@ export function ThreadPullRequestBadgeControl({
203204 number,
204205 url,
205206 status,
206 onOpenStack,
207 onOpenList,
207208 onOpenPullRequest,
208209}: {
209210 render: ReactElement<{ render?: useRender.RenderProp }>;
......@@ -211,7 +212,7 @@ export function ThreadPullRequestBadgeControl({
211212 number?: number | undefined;
212213 url?: string | undefined;
213214 status: PrStatusIndicator | null;
214 onOpenStack: () => void;
215 onOpenList: () => void;
215216 onOpenPullRequest: (event: MouseEvent<HTMLElement>) => void;
216217}) {
217218 const presentation = resolveThreadPullRequestBadgePresentation({ badge, number, url, status });
......@@ -220,9 +221,9 @@ export function ThreadPullRequestBadgeControl({
220221 <PullRequestBadge
221222 render={render}
222223 presentation={presentation}
223 isStack={badge?.kind === "stack"}
224 opensList={badge !== null && (badge.kind === "stack" || badge.others > 0)}
224225 url={url}
225 onOpenStack={onOpenStack}
226 onOpenList={onOpenList}
226227 onOpenPullRequest={onOpenPullRequest}
227228 />
228229 );
......@@ -231,26 +232,26 @@ export function ThreadPullRequestBadgeControl({
231232function PullRequestBadge({
232233 render,
233234 presentation,
234 isStack,
235 opensList,
235236 url,
236 onOpenStack,
237 onOpenList,
237238 onOpenPullRequest,
238239}: {
239240 render: ReactElement<{ render?: useRender.RenderProp }>;
240241 presentation: NonNullable<ReturnType<typeof resolveThreadPullRequestBadgePresentation>>;
241 isStack: boolean;
242 opensList: boolean;
242243 url: string | undefined;
243 onOpenStack: () => void;
244 onOpenList: () => void;
244245 onOpenPullRequest: (event: MouseEvent<HTMLElement>) => void;
245246}) {
246 const onClick = isStack
247 const onClick = opensList
247248 ? (event: MouseEvent<HTMLElement>) => {
248249 event.preventDefault();
249250 event.stopPropagation();
250 onOpenStack();
251 onOpenList();
251252 }
252253 : onOpenPullRequest;
253 const element = isStack ? (
254 const element = opensList ? (
254255 <button type="button" />
255256 ) : (
256257 <a href={url} target="_blank" rel="noopener noreferrer" />
docs/user/source-control.md+1-1
......@@ -164,7 +164,7 @@ on the Pull Requests page, **Link to thread** lets you search for an active thre
164164also lists the threads that link to it, including archived threads, so you can return to their context.
165165
166166Thread badges show a stack's layer count or the current review number with a count of additional
167links. On mobile, the Git overview lists linked reviews and their stacks; tap a review to open it.
167links. Clicking a badge with more than one review opens the **Linked pull requests** panel. On mobile, the Git overview lists linked reviews and their stacks; tap a review to open it.
168168Linking and unlinking are available in the web and desktop clients.
169169
170170The **Linked pull requests** panel lists every review and groups stacks. Unlink a review from its