History

Constrain provider update popover overflow (#2669)

Julius Marminge committed 6 months agov0.0.24-nightly.20260513.276

Commit 7e20b231 parent b83e9c92 files · +59 −8Browse files

Unified · Split

2 files
  1. apps/web/src/components/settings/ProviderInstanceCard.tsx +13 −6
  2. apps/web/src/components/settings/SettingsPanels.browser.tsx +46 −2
apps/web/src/components/settings/ProviderInstanceCard.tsx+13−6
@@ -29,6 +29,7 @@ import { Button } from "../ui/button"; · 28 unmodified lines
29import { Collapsible, CollapsibleContent } from "../ui/collapsible";29import { Collapsible, CollapsibleContent } from "../ui/collapsible";
30import { DraftInput } from "../ui/draft-input";30import { DraftInput } from "../ui/draft-input";
31import { Popover, PopoverPopup, PopoverTrigger } from "../ui/popover";31import { Popover, PopoverPopup, PopoverTrigger } from "../ui/popover";
32import { ScrollArea } from "../ui/scroll-area";
32import { Switch } from "../ui/switch";33import { Switch } from "../ui/switch";
33import { stackedThreadToast, toastManager } from "../ui/toast";34import { stackedThreadToast, toastManager } from "../ui/toast";
34import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";35import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
@@ -697,8 +698,12 @@ export function ProviderInstanceCard({ · 662 unmodified lines
697 </Button>698 </Button>
698 }699 }
699 />700 />
700 <PopoverPopup side="bottom" align="start" className="w-84">701 <PopoverPopup
701 <div className="grid gap-3">702 side="bottom"
703 align="start"
704 className="w-[min(21rem,calc(100vw-1.5rem))] [--popup-width:min(21rem,calc(100vw-1.5rem))]"
705 >
706 <div className="grid min-w-0 gap-3">
702 <div className="grid gap-0.5">707 <div className="grid gap-0.5">
703 <p className="text-[13px] font-semibold leading-tight text-foreground">708 <p className="text-[13px] font-semibold leading-tight text-foreground">
704 Update available709 Update available
@@ -735,10 +740,12 @@ export function ProviderInstanceCard({ · 30 unmodified lines
735 </div>740 </div>
736 ) : null}741 ) : null}
737 {updateCommand ? (742 {updateCommand ? (
738 <div className="flex items-center gap-1 rounded-md border border-border/70 bg-muted/40 py-0.5 pr-0.5 pl-2">743 <div className="flex min-w-0 items-center gap-1 rounded-md border border-border/70 bg-muted/40 py-0.5 pr-0.5 pl-2">
739 <code className="min-w-0 flex-1 overflow-x-auto whitespace-nowrap font-mono text-[11px] text-foreground [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">744 <ScrollArea scrollFade className="h-8 min-w-0 flex-1 rounded-none">
740 {updateCommand}745 <code className="flex h-full w-max items-center whitespace-nowrap pr-3 font-mono text-[11px] text-foreground">
741 </code>746 {updateCommand}
747 </code>
748 </ScrollArea>
742 <Tooltip>749 <Tooltip>
743 <TooltipTrigger750 <TooltipTrigger
744 render={751 render={
apps/web/src/components/settings/SettingsPanels.browser.tsx+46−2
@@ -236,7 +236,10 @@ function createBaseServerConfig(): ServerConfig { · 235 unmodified lines
236 };236 };
237}237}
238 238
239function createOutdatedProvider(driver: string): ServerProvider {239function createOutdatedProvider(
240 driver: string,
241 updateCommand = "npm install -g openai/codex@latest",
242): ServerProvider {
240 return {243 return {
241 instanceId: ProviderInstanceId.make(driver),244 instanceId: ProviderInstanceId.make(driver),
242 driver: ProviderDriverKind.make(driver),245 driver: ProviderDriverKind.make(driver),
@@ -255,7 +258,7 @@ function createOutdatedProvider(driver: string): ServerProvider { · 12 unmodified lines
255 latestVersion: "1.1.0",258 latestVersion: "1.1.0",
256 message: "Update available.",259 message: "Update available.",
257 checkedAt: "2026-05-04T10:00:00.000Z",260 checkedAt: "2026-05-04T10:00:00.000Z",
258 updateCommand: "npm install -g openai/codex@latest",261 updateCommand,
259 canUpdate: true,262 canUpdate: true,
260 },263 },
261 };264 };
@@ -1155,6 +1158,47 @@ describe("GeneralSettingsPanel observability", () => { · 893 unmodified lines
1155 instanceId: ProviderInstanceId.make("codex"),1158 instanceId: ProviderInstanceId.make("codex"),
1156 });1159 });
1157 });1160 });
1161
1162 it("keeps long provider update commands inside the fixed-width popover", async () => {
1163 const longUpdateCommand =
1164 "npm install -g @anthropic-ai/claude-code@latest --registry=https://registry.npmjs.org --cache=/tmp/t3code-provider-update-cache";
1165
1166 setServerConfigSnapshot({
1167 ...createBaseServerConfig(),
1168 providers: [createOutdatedProvider("codex", longUpdateCommand)],
1169 });
1170
1171 mounted = await render(
1172 <AppAtomRegistryProvider>
1173 <ProviderSettingsPanel />
1174 </AppAtomRegistryProvider>,
1175 );
1176
1177 await page.getByRole("button", { name: "Update available — view details" }).click();
1178 await expect.element(page.getByText(longUpdateCommand)).toBeInTheDocument();
1179
1180 await vi.waitFor(() => {
1181 const popup = document.querySelector<HTMLElement>('[data-slot="popover-popup"]');
1182 const commandCode = Array.from(document.querySelectorAll<HTMLElement>("code")).find(
1183 (element) => element.textContent === longUpdateCommand,
1184 );
1185 const scrollViewport = commandCode?.closest<HTMLElement>(
1186 '[data-slot="scroll-area-viewport"]',
1187 );
1188
1189 expect(popup).toBeTruthy();
1190 expect(commandCode).toBeTruthy();
1191 expect(scrollViewport).toBeTruthy();
1192
1193 const popupRect = popup!.getBoundingClientRect();
1194 const viewportRect = scrollViewport!.getBoundingClientRect();
1195
1196 expect(popupRect.width).toBeGreaterThan(300);
1197 expect(popupRect.width).toBeLessThanOrEqual(337);
1198 expect(viewportRect.right).toBeLessThanOrEqual(popupRect.right + 0.5);
1199 expect(scrollViewport!.scrollWidth).toBeGreaterThan(scrollViewport!.clientWidth);
1200 });
1201 });
1158});1202});
1159 1203
1160describe("SourceControlSettingsPanel discovery states", () => {1204describe("SourceControlSettingsPanel discovery states", () => {