import React, { Suspense, useEffect } from "react";
import Login from "../../app/components/modals/Login";
import LoginForm from "../../app/components/modals/Login/LoginForm";
/**
* Storybook runs outside Next.js request context; successful "Send link" needs fetch mocked
* because `requestMagicLink` POSTs to `/api/auth/magic-link/request`.
*/
function MagicLinkFetchMock({ children }: { children: React.ReactNode }) {
useEffect(() => {
const orig = globalThis.fetch;
globalThis.fetch = async (
input: RequestInfo | URL,
init?: RequestInit,
): Promise => {
const url =
typeof input === "string"
? input
: input instanceof URL
? input.href
: input.url;
if (url.includes("/api/auth/magic-link/request")) {
return new Response("{}", {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
return orig(input as Request, init);
};
return () => {
globalThis.fetch = orig;
};
}, []);
return <>{children}>;
}
export default {
title: "Components/Modals/Login",
component: Login,
parameters: {
layout: "fullscreen",
nextjs: {
appDirectory: true,
navigation: {
pathname: "/login",
},
},
docs: {
description: {
component:
"Full-page style login shell (yellow backdrop) with modal card. Uses magic-link `LoginForm` inside. Matches `/login` and header modal usage.",
},
},
},
decorators: [
(Story: () => React.ReactNode) => (
),
],
tags: ["autodocs"],
};
export const ModalChromeOnly = {
name: "Modal (placeholder content)",
render: () => (
{}}
ariaLabelledBy="login-modal-heading"
belowCard={
← Back to home
}
>
Placeholder body — use "With magic link form" for the real
flow.
),
};
export const WithMagicLinkForm = {
name: "With magic link form",
render: () => (
{}}
ariaLabelledBy="login-modal-heading"
belowCard={
← Back to home
}
>
Loading…
}>
),
};
export const FormOnly = {
name: "Login form (card inset)",
parameters: {
docs: {
description: {
story:
"Form only, for inspecting copy and layout without the modal chrome. Wrap in `Login` in the app.",
},
},
},
render: () => (
Loading…}>
),
};