Compare commits

...
4 changed files with 77 additions and 10 deletions
@@ -0,0 +1 @@
OSS auth logo alignment on the sign-in page
@@ -0,0 +1,70 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { AuthLayout } from "./auth-layout";
import { PublicAuthShell } from "./public-auth-shell";
vi.mock("@/components/ThemeSwitch", () => ({
ThemeSwitch: ({ "aria-label": ariaLabel }: { "aria-label"?: string }) => (
<button type="button" aria-label={ariaLabel} />
),
}));
describe("AuthLayout", () => {
it("renders the auth card title and content", () => {
// Given
const title = "Sign in to Prowler";
// When
render(
<AuthLayout title={title}>
<form aria-label="Sign in form">Form content</form>
</AuthLayout>,
);
// Then
expect(screen.getByText(title)).toBeVisible();
expect(screen.getByRole("form", { name: "Sign in form" })).toBeVisible();
});
it("renders the brand in the centered auth layout flow above the card", () => {
// Given
const title = "Sign in to Prowler";
// When
render(
<AuthLayout title={title}>
<form aria-label="Sign in form">Form content</form>
</AuthLayout>,
);
// Then
const brand = screen.getByRole("img", { name: /prowler/i });
const brandWrapper = brand.parentElement;
const cardTitle = screen.getByText(title);
expect(brand).toBeVisible();
expect(brandWrapper).toHaveClass("relative", "z-10", "mb-8", "w-[200px]");
expect(brandWrapper).not.toHaveClass("absolute", "top-8");
expect(brand.compareDocumentPosition(cardTitle)).toBe(
Node.DOCUMENT_POSITION_FOLLOWING,
);
});
});
describe("PublicAuthShell", () => {
it("does not render a shell-level absolute brand", () => {
// Given / When
render(
<PublicAuthShell>
<main>Auth page</main>
</PublicAuthShell>,
);
// Then
expect(
screen.queryByRole("img", { name: /prowler/i }),
).not.toBeInTheDocument();
expect(screen.getByText("Auth page")).toBeVisible();
});
});
+5
View File
@@ -1,5 +1,6 @@
import { ReactNode } from "react";
import { ProwlerBrand } from "@/components/icons";
import { ThemeSwitch } from "@/components/ThemeSwitch";
interface AuthLayoutProps {
@@ -20,6 +21,10 @@ export const AuthLayout = ({ title, children }: AuthLayoutProps) => {
}}
></div>
<div className="relative z-10 mb-8 w-[200px]">
<ProwlerBrand className="w-full" />
</div>
{/* Auth Form Container */}
<div className="border-border-neutral-secondary dark:bg-bg-neutral-primary/85 relative z-10 flex w-full max-w-sm flex-col gap-4 rounded-[14px] border bg-white/90 px-8 py-10 shadow-sm md:max-w-md">
{/* Header with Title and Theme Toggle */}
+1 -10
View File
@@ -1,18 +1,9 @@
import type { ReactNode } from "react";
import { ProwlerBrand } from "@/components/icons";
interface PublicAuthShellProps {
children: ReactNode;
}
export const PublicAuthShell = ({ children }: PublicAuthShellProps) => {
return (
<div className="relative min-h-screen">
<div className="pointer-events-none absolute top-8 left-1/2 z-20 w-[200px] -translate-x-1/2">
<ProwlerBrand className="w-full" />
</div>
{children}
</div>
);
return <div className="relative min-h-screen">{children}</div>;
};