authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-03 00:21:27-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-03 00:21:27-04:00
log2a3cfe456375fd34b906f849b04706109dc74170
tree9dc7e6c2c087480bd86730495b6c994883ce1a72
parent5f84efa1ec2fb3cd6f6c54545cc34bb77ac1ddb8
signature Signed by PGP key B5690EEEBB952194

fix(web): collapse PR header actions to icons when narrow (#9334)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

1 files changed, 179 insertions(+), 54 deletions(-)

apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx+179-54
...@@ -1296,8 +1296,8 @@ export function PullRequestDetailPanel({...@@ -1296,8 +1296,8 @@ export function PullRequestDetailPanel({
12961296
1297 return (1297 return (
1298 <div className="flex h-full min-h-0 w-full flex-col bg-background">1298 <div className="flex h-full min-h-0 w-full flex-col bg-background">
1299 <div className="grid shrink-0 grid-cols-[minmax(0,1fr)_auto] items-start gap-x-2 border-b border-border/60">1299 <div className="@container/pr-header grid min-w-0 shrink-0 grid-cols-[minmax(0,1fr)_auto] items-start gap-x-2 border-b border-border/60">
1300 <div className="ml-4 grid h-7 min-w-0 items-center">1300 <div className="ml-4 grid h-7 min-w-0 items-center overflow-hidden">
1301 <div1301 <div
1302 aria-hidden={condensed}1302 aria-hidden={condensed}
1303 inert={condensed}1303 inert={condensed}
...@@ -1402,7 +1402,7 @@ export function PullRequestDetailPanel({...@@ -1402,7 +1402,7 @@ export function PullRequestDetailPanel({
1402 ) : null}1402 ) : null}
1403 </div>1403 </div>
1404 </div>1404 </div>
1405 <div className="mr-4 flex h-7 min-w-0 flex-nowrap items-center justify-end gap-1">1405 <div className="mr-4 flex h-7 shrink-0 items-center justify-end gap-1">
1406 {detail ? (1406 {detail ? (
1407 <>1407 <>
1408 {/* Checking a pull request out is the reason to open one here at all, so it is a1408 {/* Checking a pull request out is the reason to open one here at all, so it is a
...@@ -1415,9 +1415,17 @@ export function PullRequestDetailPanel({...@@ -1415,9 +1415,17 @@ export function PullRequestDetailPanel({
1415 <MenuTrigger1415 <MenuTrigger
1416 disabled={handoff !== null}1416 disabled={handoff !== null}
1417 render={1417 render={
1418 <Button size="xs" variant="outline">1418 <Button
1419 size="xs"
1420 variant="outline"
1421 aria-label={
1422 handoff?.startsWith("checkout") ? "Checking out..." : "Check out"
1423 }
1424 >
1419 <GitBranchIcon aria-hidden className="size-3.5" />1425 <GitBranchIcon aria-hidden className="size-3.5" />
1420 {handoff?.startsWith("checkout") ? "Checking out..." : "Check out"}1426 <span className="@max-[35rem]/pr-header:hidden">
1427 {handoff?.startsWith("checkout") ? "Checking out..." : "Check out"}
1428 </span>
1421 <ChevronDownIcon aria-hidden className="size-3.5 text-muted-foreground" />1429 <ChevronDownIcon aria-hidden className="size-3.5 text-muted-foreground" />
1422 </Button>1430 </Button>
1423 }1431 }
...@@ -1453,17 +1461,39 @@ export function PullRequestDetailPanel({...@@ -1453,17 +1461,39 @@ export function PullRequestDetailPanel({
1453 </Menu>1461 </Menu>
1454 ) : null}1462 ) : null}
1455 {workflowApprovalsRequired > 0 && can("approve-workflows") ? (1463 {workflowApprovalsRequired > 0 && can("approve-workflows") ? (
1456 <Button1464 <Tooltip>
1457 size="xs"1465 <TooltipTrigger
1458 variant="warning-outline"1466 render={
1459 disabled={actionPending}1467 <span className="inline-flex shrink-0">
1460 onClick={() => setConfirmation({ open: true, action: "approve-workflows" })}1468 <Button
1461 >1469 size="xs"
1462 <PlayIcon className="size-3.5" />1470 variant="warning-outline"
1463 {pendingAction === "approve-workflows"1471 disabled={actionPending}
1464 ? "Approving..."1472 onClick={() =>
1465 : "Approve workflows to run"}1473 setConfirmation({ open: true, action: "approve-workflows" })
1466 </Button>1474 }
1475 aria-label={
1476 pendingAction === "approve-workflows"
1477 ? "Approving..."
1478 : "Approve workflows to run"
1479 }
1480 >
1481 <PlayIcon aria-hidden className="size-3.5" />
1482 <span className="@max-[40rem]/pr-header:hidden">
1483 {pendingAction === "approve-workflows"
1484 ? "Approving..."
1485 : "Approve workflows to run"}
1486 </span>
1487 </Button>
1488 </span>
1489 }
1490 />
1491 <TooltipPopup side="top">
1492 {pendingAction === "approve-workflows"
1493 ? "Approving..."
1494 : "Approve workflows to run"}
1495 </TooltipPopup>
1496 </Tooltip>
1467 ) : null}1497 ) : null}
1468 {/* Said where the Merge button is, because it is the answer to why nobody has1498 {/* Said where the Merge button is, because it is the answer to why nobody has
1469 pressed it: the merge is already asked for, and the host is holding it. */}1499 pressed it: the merge is already asked for, and the host is holding it. */}
...@@ -1471,62 +1501,150 @@ export function PullRequestDetailPanel({...@@ -1471,62 +1501,150 @@ export function PullRequestDetailPanel({
1471 <Tooltip>1501 <Tooltip>
1472 <TooltipTrigger1502 <TooltipTrigger
1473 render={1503 render={
1474 <Badge size="control" variant="info">1504 <Badge
1505 size="control"
1506 variant="info"
1507 role="img"
1508 aria-label={armedAutoMergeLabel}
1509 >
1475 <GitMergeIcon aria-hidden className="size-3.5" />1510 <GitMergeIcon aria-hidden className="size-3.5" />
1476 {armedAutoMergeLabel}1511 <span className="@max-[30rem]/pr-header:hidden">{armedAutoMergeLabel}</span>
1477 </Badge>1512 </Badge>
1478 }1513 }
1479 />1514 />
1480 <TooltipPopup side="top">1515 <TooltipPopup side="top">
1481 The host will merge this on its own once its requirements are met1516 {armedAutoMergeLabel}: the host will merge this on its own once its requirements
1517 are met
1482 </TooltipPopup>1518 </TooltipPopup>
1483 </Tooltip>1519 </Tooltip>
1484 ) : null}1520 ) : null}
1485 {primaryAction === "resolve" ? (1521 {primaryAction === "resolve" ? (
1486 <Button1522 <Tooltip>
1487 size="xs"1523 <TooltipTrigger
1488 variant="destructive-outline"1524 render={
1489 disabled={handoff !== null}1525 <span className="inline-flex shrink-0">
1490 onClick={startResolveConflicts}1526 <Button
1491 >1527 size="xs"
1492 <TriangleAlertIcon className="size-3.5" />1528 variant="destructive-outline"
1493 {handoff === "conflicts" ? "Preparing..." : "Resolve conflicts"}1529 disabled={handoff !== null}
1494 </Button>1530 onClick={startResolveConflicts}
1531 aria-label={
1532 handoff === "conflicts" ? "Preparing..." : "Resolve conflicts"
1533 }
1534 >
1535 <TriangleAlertIcon aria-hidden className="size-3.5" />
1536 <span className="@max-[30rem]/pr-header:hidden">
1537 {handoff === "conflicts" ? "Preparing..." : "Resolve conflicts"}
1538 </span>
1539 </Button>
1540 </span>
1541 }
1542 />
1543 <TooltipPopup side="top">
1544 {handoff === "conflicts" ? "Preparing..." : "Resolve conflicts"}
1545 </TooltipPopup>
1546 </Tooltip>
1495 ) : primaryAction === "ready" ? (1547 ) : primaryAction === "ready" ? (
1496 <Button size="xs" disabled={actionPending} onClick={() => void perform("ready")}>1548 <Tooltip>
1497 Ready for review1549 <TooltipTrigger
1498 </Button>1550 render={
1551 <span className="inline-flex shrink-0">
1552 <Button
1553 size="xs"
1554 disabled={actionPending}
1555 onClick={() => void perform("ready")}
1556 aria-label="Ready for review"
1557 >
1558 <GitPullRequestIcon
1559 aria-hidden
1560 className="hidden size-3.5 @max-[30rem]/pr-header:inline"
1561 />
1562 <span className="@max-[30rem]/pr-header:hidden">Ready for review</span>
1563 </Button>
1564 </span>
1565 }
1566 />
1567 <TooltipPopup side="top">Ready for review</TooltipPopup>
1568 </Tooltip>
1499 ) : primaryAction === "enable-auto-merge" ? (1569 ) : primaryAction === "enable-auto-merge" ? (
1500 <Button1570 <Tooltip>
1501 size="xs"1571 <TooltipTrigger
1502 disabled={actionPending}1572 render={
1503 onClick={() => setConfirmation({ open: true, action: "enable-auto-merge" })}1573 <span className="inline-flex shrink-0">
1504 >1574 <Button
1505 <GitMergeIcon className="size-3.5" />1575 size="xs"
1506 {pendingAction === "enable-auto-merge" ? "Enabling..." : pendingAutoMergeLabel}1576 disabled={actionPending}
1507 </Button>1577 onClick={() =>
1578 setConfirmation({ open: true, action: "enable-auto-merge" })
1579 }
1580 aria-label={
1581 pendingAction === "enable-auto-merge"
1582 ? "Enabling..."
1583 : pendingAutoMergeLabel
1584 }
1585 >
1586 <GitMergeIcon aria-hidden className="size-3.5" />
1587 <span className="@max-[30rem]/pr-header:hidden">
1588 {pendingAction === "enable-auto-merge"
1589 ? "Enabling..."
1590 : pendingAutoMergeLabel}
1591 </span>
1592 </Button>
1593 </span>
1594 }
1595 />
1596 <TooltipPopup side="top">
1597 {pendingAction === "enable-auto-merge" ? "Enabling..." : pendingAutoMergeLabel}
1598 </TooltipPopup>
1599 </Tooltip>
1508 ) : primaryAction === "auto-merge-armed" ? (1600 ) : primaryAction === "auto-merge-armed" ? (
1509 <Tooltip>1601 <Tooltip>
1510 <TooltipTrigger1602 <TooltipTrigger
1511 render={1603 render={
1512 <Badge size="control" variant="info">1604 <Badge
1513 <GitMergeIcon className="size-3.5" />1605 size="control"
1514 {armedAutoMergeLabel}1606 variant="info"
1607 role="img"
1608 aria-label={armedAutoMergeLabel}
1609 >
1610 <GitMergeIcon aria-hidden className="size-3.5" />
1611 <span className="@max-[30rem]/pr-header:hidden">{armedAutoMergeLabel}</span>
1515 </Badge>1612 </Badge>
1516 }1613 }
1517 />1614 />
1518 <TooltipPopup side="top">1615 <TooltipPopup side="top">
1519 The host will merge this on its own once its requirements are met1616 {armedAutoMergeLabel}: the host will merge this on its own once its requirements
1617 are met
1520 </TooltipPopup>1618 </TooltipPopup>
1521 </Tooltip>1619 </Tooltip>
1522 ) : primaryAction === "merge" ? (1620 ) : primaryAction === "merge" ? (
1523 <Button1621 <Tooltip>
1524 size="xs"1622 <TooltipTrigger
1525 disabled={actionPending}1623 render={
1526 onClick={() => setConfirmation({ open: true, action: "merge" })}1624 <span className="inline-flex shrink-0">
1527 >1625 <Button
1528 {pendingAction === "merge" ? "Merging..." : selectedMergeMethodLabel}1626 size="xs"
1529 </Button>1627 disabled={actionPending}
1628 onClick={() => setConfirmation({ open: true, action: "merge" })}
1629 aria-label={
1630 pendingAction === "merge" ? "Merging..." : selectedMergeMethodLabel
1631 }
1632 >
1633 <GitMergeIcon
1634 aria-hidden
1635 className="hidden size-3.5 @max-[30rem]/pr-header:inline"
1636 />
1637 <span className="@max-[30rem]/pr-header:hidden">
1638 {pendingAction === "merge" ? "Merging..." : selectedMergeMethodLabel}
1639 </span>
1640 </Button>
1641 </span>
1642 }
1643 />
1644 <TooltipPopup side="top">
1645 {pendingAction === "merge" ? "Merging..." : selectedMergeMethodLabel}
1646 </TooltipPopup>
1647 </Tooltip>
1530 ) : (primaryAction === "merged" || primaryAction === "closed") &&1648 ) : (primaryAction === "merged" || primaryAction === "closed") &&
1531 statePresentation !== null ? (1649 statePresentation !== null ? (
1532 <Badge size="control" variant="outline" className={statePresentation.toneClassName}>1650 <Badge size="control" variant="outline" className={statePresentation.toneClassName}>
...@@ -1857,10 +1975,17 @@ export function PullRequestDetailPanel({...@@ -1857,10 +1975,17 @@ export function PullRequestDetailPanel({
1857 {detail ? (1975 {detail ? (
1858 <div className="col-span-2 mt-1 min-w-0 px-4 pb-4">1976 <div className="col-span-2 mt-1 min-w-0 px-4 pb-4">
1859 {titleDraft === null ? (1977 {titleDraft === null ? (
1860 <div className="group flex min-w-0 items-start gap-1">1978 <div className="group flex min-w-0 items-center gap-1">
1861 <h1 className="min-w-0 flex-1 text-base font-semibold leading-snug">1979 <Tooltip>
1862 {detail.title}1980 <TooltipTrigger
1863 </h1>1981 render={
1982 <h1 className="min-w-0 flex-1 truncate text-base font-semibold leading-snug">
1983 {detail.title}
1984 </h1>
1985 }
1986 />
1987 <TooltipPopup side="top">{detail.title}</TooltipPopup>
1988 </Tooltip>
1864 {canEditPullRequestChangeRequest(detail) ? (1989 {canEditPullRequestChangeRequest(detail) ? (
1865 <Button1990 <Button
1866 size="icon-xs"1991 size="icon-xs"