Fit the login form to the picture's box
The form now sits in the same 16:9 box as the snapshot, so a card keeps its height whichever it shows. A compact variant drops the aside about which user to create and tightens the spacing, since that space is fixed; the camera page keeps the full form, where there is room. Anything that still doesn't fit scrolls inside the box rather than stretching the card. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6a663a5d82
commit
f953f38716
@ -270,6 +270,10 @@ describe("CameraCard", () => {
|
||||
expect(screen.queryByRole("link", { name: "Pop out Porch" })).toBeNull();
|
||||
// The status still says what is wrong, where the picture would have been.
|
||||
expect(container.querySelector("[data-status='auth']")).toBeTruthy();
|
||||
|
||||
// Same box as the picture, and the aside that wouldn't fit is left out.
|
||||
expect(container.querySelector(".aspect-video")).toBeTruthy();
|
||||
expect(container.textContent).not.toContain("Use the ONVIF user you created");
|
||||
});
|
||||
|
||||
it("keeps a username the user typed over the saved one", async () => {
|
||||
|
||||
@ -178,14 +178,17 @@ export default function CameraCard({
|
||||
<li className="flex flex-col gap-3 rounded-lg border border-zinc-200 p-4 dark:border-zinc-800">
|
||||
{/* A camera that can't log in has no picture to show, so the form takes its place. */}
|
||||
{showLogin ? (
|
||||
<div className="flex flex-col gap-2 rounded bg-zinc-100 p-3 dark:bg-zinc-900">
|
||||
<div className="flex aspect-video flex-col gap-2 overflow-hidden rounded bg-zinc-100 p-2 dark:bg-zinc-900">
|
||||
<StatusBadge status={status} className="self-start" />
|
||||
<CredentialsForm
|
||||
cam={cam}
|
||||
reason={problem?.kind === "auth" ? problem.message : undefined}
|
||||
onSaved={() => setEditingLogin(false)}
|
||||
onCancel={problem?.kind === "auth" ? undefined : () => setEditingLogin(false)}
|
||||
/>
|
||||
<div className="min-h-0 flex-1 overflow-y-auto">
|
||||
<CredentialsForm
|
||||
cam={cam}
|
||||
reason={problem?.kind === "auth" ? problem.message : undefined}
|
||||
onSaved={() => setEditingLogin(false)}
|
||||
onCancel={problem?.kind === "auth" ? undefined : () => setEditingLogin(false)}
|
||||
compact
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Snapshot
|
||||
|
||||
@ -31,12 +31,15 @@ export function CredentialsForm({
|
||||
reason,
|
||||
onSaved,
|
||||
onCancel,
|
||||
compact = false,
|
||||
}: {
|
||||
cam: CameraSummary;
|
||||
reason?: string;
|
||||
/** Called after a save or clear; the camera's queries have already been reset. */
|
||||
onSaved: () => void;
|
||||
onCancel?: () => void;
|
||||
/** For the dashboard card, where the form stands in for the picture. */
|
||||
compact?: boolean;
|
||||
}) {
|
||||
const status = useCredentialStatus(cam.id);
|
||||
const save = useSaveCredentials(cam.id);
|
||||
@ -63,16 +66,22 @@ export function CredentialsForm({
|
||||
const busy = save.isPending || clear.isPending;
|
||||
|
||||
return (
|
||||
<form onSubmit={submit} className="flex flex-col gap-2 rounded border border-amber-400/60 p-3 text-sm">
|
||||
<form
|
||||
onSubmit={submit}
|
||||
className={`flex h-full flex-col rounded border border-amber-400/60 text-sm ${compact ? "gap-1.5 p-2" : "gap-2 p-3"}`}
|
||||
>
|
||||
<div className="flex items-center gap-1.5 font-medium">
|
||||
<KeyRound aria-hidden className="size-4 shrink-0" />
|
||||
Camera login
|
||||
</div>
|
||||
{reason && <div className="text-xs text-zinc-500">{reason}</div>}
|
||||
<div className="text-xs text-zinc-500">
|
||||
Use the ONVIF user you created on the camera (
|
||||
<WebUiLink cam={cam}>camera web page</WebUiLink>).
|
||||
</div>
|
||||
{/* On a card this has to fit where the picture would be, so the aside goes. */}
|
||||
{!compact && (
|
||||
<div className="text-xs text-zinc-500">
|
||||
Use the ONVIF user you created on the camera (
|
||||
<WebUiLink cam={cam}>camera web page</WebUiLink>).
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
value={username}
|
||||
onChange={(e) => setTypedUsername(e.target.value)}
|
||||
|
||||
@ -59,6 +59,8 @@ describe("CameraLoginControls", () => {
|
||||
expect(screen.queryByText("Camera login")).toBeNull();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Change login" }));
|
||||
expect(await screen.findByText("Camera login")).toBeTruthy();
|
||||
// With room to spare here, the form keeps its aside about which user to use.
|
||||
expect(screen.getByText(/Use the ONVIF user you created/)).toBeTruthy();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Cancel" }));
|
||||
expect(screen.queryByText("Camera login")).toBeNull();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user