From 83cce344e8638ae330fa80b497d8d04de6b763fb Mon Sep 17 00:00:00 2001 From: LukeGus Date: Fri, 9 Oct 2026 09:29:27 -0500 Subject: [PATCH] fix: link server wizard always opens inline, onboarding card grows for it --- src/ui/onboarding/OnboardingStage.tsx | 6 +- src/ui/settings/sync/LinkServerDialog.tsx | 66 +++++-------------- .../settings/sync/LinkServerDialog.test.tsx | 31 +++++++++ 3 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 src/ui/tests/settings/sync/LinkServerDialog.test.tsx diff --git a/src/ui/onboarding/OnboardingStage.tsx b/src/ui/onboarding/OnboardingStage.tsx index 809f4d1c9..470ebc856 100644 --- a/src/ui/onboarding/OnboardingStage.tsx +++ b/src/ui/onboarding/OnboardingStage.tsx @@ -117,6 +117,7 @@ export function OnboardingStage({ }, [allSteps, ctx]); const [currentId, setCurrentId] = useState(null); + const [viewOpen, setViewOpen] = useState(false); const lastPosition = useRef(0); const [confirmed, setConfirmed] = useState>(() => new Set()); const [canContinue, setCanContinueMap] = useState>( @@ -280,7 +281,10 @@ export function OnboardingStage({ setViewOpen(!!editing)} + className={`relative flex flex-col border border-border bg-card ${ + viewOpen ? "min-h-[70vh]" : "" + }`} >
{Icon && } diff --git a/src/ui/settings/sync/LinkServerDialog.tsx b/src/ui/settings/sync/LinkServerDialog.tsx index a34969145..57b8db76f 100644 --- a/src/ui/settings/sync/LinkServerDialog.tsx +++ b/src/ui/settings/sync/LinkServerDialog.tsx @@ -1,18 +1,11 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { InlineView, useSurfaceKind } from "@/components/surface/surface-scope"; +import { InlineView } from "@/components/surface/surface-scope"; import { toast } from "sonner"; import { AlertCircle, Loader2, X } from "lucide-react"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { Switch } from "@/components/switch"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/dialog"; import { ElectronLoginForm } from "@/auth/ElectronLoginForm"; import { completeLink, @@ -224,7 +217,6 @@ export function LinkServerDialog({ 0, ); const wide = step === "signin"; - const inSurface = useSurfaceKind() !== null; const title = relogin ? t("sync.wizard.reloginTitle") @@ -443,47 +435,21 @@ export function LinkServerDialog({ ); - // From a sidebar panel the wizard takes over the panel. Outside one (the - // first-run setup) it stays a dialog. - if (inSurface) { - return ( - - {!wide && ( -

- {description} {step === "server" && } -

- )} - {content} -
- ); - } - + // The wizard takes over the panel, tab or onboarding step it opens from. return ( - - wide && event.preventDefault()} - onInteractOutside={(event) => wide && event.preventDefault()} - > - - {title} - - {description} {step === "server" && } - - - - {content} - - + + {!wide && ( +

+ {description} {step === "server" && } +

+ )} + {content} +
); } diff --git a/src/ui/tests/settings/sync/LinkServerDialog.test.tsx b/src/ui/tests/settings/sync/LinkServerDialog.test.tsx new file mode 100644 index 000000000..fc79c405c --- /dev/null +++ b/src/ui/tests/settings/sync/LinkServerDialog.test.tsx @@ -0,0 +1,31 @@ +import { cleanup, render, screen } from "@testing-library/react"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +vi.mock("@/api/sync-api", () => ({ + probeServer: vi.fn(), + getLinkPreview: vi.fn(), + completeLink: vi.fn(), + reloginLink: vi.fn(), +})); +vi.mock("@/auth/ElectronLoginForm", () => ({ ElectronLoginForm: () => null })); +vi.mock("sonner", () => ({ toast: { success: vi.fn(), error: vi.fn() } })); +vi.mock("react-i18next", () => ({ + useTranslation: () => ({ t: (key: string) => key }), +})); + +const { SurfaceScope } = await import("@/components/surface/surface-scope"); +const { LinkServerDialog } = await import("@/settings/sync/LinkServerDialog"); + +afterEach(cleanup); + +describe("LinkServerDialog", () => { + it("opens inline in the surface instead of a popup", () => { + render( + + {}} onLinked={() => {}} /> + , + ); + expect(screen.getByText("sync.wizard.title")).toBeTruthy(); + expect(screen.queryByRole("dialog")).toBeNull(); + }); +});