aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Joe Banks <[email protected]>2024-08-24 02:52:07 +0100
committerGravatar Joe Banks <[email protected]>2024-08-24 02:52:07 +0100
commit4f8835a48ab627e2d1d18a1eb2f537210f730e11 (patch)
tree19afdf07b4674de1dc761a06782e43ac432cbf6d /src
parentupdate keycloakify (diff)
Add some pydis styling
Diffstat (limited to 'src')
-rw-r--r--src/login/KcContext.ts1
-rw-r--r--src/login/KcPageStory.tsx3
-rw-r--r--src/login/pages/Code.stories.tsx18
-rw-r--r--src/login/pages/DeleteAccountConfirm.stories.tsx18
-rw-r--r--src/login/pages/DeleteCredential.stories.tsx18
-rw-r--r--src/login/pages/Error.stories.tsx28
-rw-r--r--src/login/pages/FrontchannelLogout.stories.tsx18
-rw-r--r--src/login/pages/IdpReviewUserProfile.stories.tsx18
-rw-r--r--src/login/pages/Info.stories.tsx57
-rw-r--r--src/login/pages/Login.stories.tsx233
-rw-r--r--src/login/pages/LoginConfigTotp.stories.tsx43
-rw-r--r--src/login/pages/LoginIdpLinkConfirm.stories.tsx18
-rw-r--r--src/login/pages/LoginIdpLinkEmail.stories.tsx18
-rw-r--r--src/login/pages/LoginOauth2DeviceVerifyUserCode.stories.tsx17
-rw-r--r--src/login/pages/LoginOauthGrant.stories.tsx18
-rw-r--r--src/login/pages/LoginOtp.stories.tsx18
-rw-r--r--src/login/pages/LoginPageExpired.stories.tsx18
-rw-r--r--src/login/pages/LoginPassword.stories.tsx18
-rw-r--r--src/login/pages/LoginRecoveryAuthnCodeConfig.stories.tsx18
-rw-r--r--src/login/pages/LoginRecoveryAuthnCodeInput.stories.tsx18
-rw-r--r--src/login/pages/LoginResetOtp.stories.tsx18
-rw-r--r--src/login/pages/LoginResetPassword.stories.tsx31
-rw-r--r--src/login/pages/LoginUpdatePassword.stories.tsx18
-rw-r--r--src/login/pages/LoginUpdateProfile.stories.tsx18
-rw-r--r--src/login/pages/LoginUsername.stories.tsx31
-rw-r--r--src/login/pages/LoginVerifyEmail.stories.tsx30
-rw-r--r--src/login/pages/LoginX509Info.stories.tsx18
-rw-r--r--src/login/pages/LogoutConfirm.stories.tsx18
-rw-r--r--src/login/pages/Register.stories.tsx187
-rw-r--r--src/login/pages/SamlPostForm.stories.tsx18
-rw-r--r--src/login/pages/SelectAuthenticator.stories.tsx84
-rw-r--r--src/login/pages/Terms.stories.tsx47
-rw-r--r--src/login/pages/UpdateEmail.stories.tsx18
-rw-r--r--src/login/pages/WebauthnAuthenticate.stories.tsx18
-rw-r--r--src/login/pages/WebauthnError.stories.tsx18
-rw-r--r--src/login/pages/WebauthnRegister.stories.tsx18
36 files changed, 1206 insertions, 0 deletions
diff --git a/src/login/KcContext.ts b/src/login/KcContext.ts
index a130466..abe9ebb 100644
--- a/src/login/KcContext.ts
+++ b/src/login/KcContext.ts
@@ -5,6 +5,7 @@ import type { KcEnvName, ThemeName } from "../kc.gen";
export type KcContextExtension = {
themeName: ThemeName;
properties: Record<KcEnvName, string> & {};
+ realm: Record<KcEnvName, string> & {};
};
export type KcContextExtensionPerPage = {};
diff --git a/src/login/KcPageStory.tsx b/src/login/KcPageStory.tsx
index 2a69b6a..5d0eb3b 100644
--- a/src/login/KcPageStory.tsx
+++ b/src/login/KcPageStory.tsx
@@ -9,6 +9,9 @@ const kcContextExtension: KcContextExtension = {
themeName: themeNames[0],
properties: {
...kcEnvDefaults
+ },
+ realm: {
+ displayNameHtml: '<div class="kc-logo-text"><span>Python Discord</span></div>',
}
};
const kcContextExtensionPerPage: KcContextExtensionPerPage = {};
diff --git a/src/login/pages/Code.stories.tsx b/src/login/pages/Code.stories.tsx
new file mode 100644
index 0000000..0fcdf87
--- /dev/null
+++ b/src/login/pages/Code.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "code.ftl" });
+
+const meta = {
+ title: "login/code.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/DeleteAccountConfirm.stories.tsx b/src/login/pages/DeleteAccountConfirm.stories.tsx
new file mode 100644
index 0000000..501fa0c
--- /dev/null
+++ b/src/login/pages/DeleteAccountConfirm.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "delete-account-confirm.ftl" });
+
+const meta = {
+ title: "login/delete-account-confirm.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/DeleteCredential.stories.tsx b/src/login/pages/DeleteCredential.stories.tsx
new file mode 100644
index 0000000..7b211a5
--- /dev/null
+++ b/src/login/pages/DeleteCredential.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "delete-credential.ftl" });
+
+const meta = {
+ title: "login/delete-credential.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/Error.stories.tsx b/src/login/pages/Error.stories.tsx
new file mode 100644
index 0000000..7b12410
--- /dev/null
+++ b/src/login/pages/Error.stories.tsx
@@ -0,0 +1,28 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "error.ftl" });
+
+const meta = {
+ title: "login/error.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
+
+export const WithAnotherMessage: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ message: { summary: "With another error message" }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/FrontchannelLogout.stories.tsx b/src/login/pages/FrontchannelLogout.stories.tsx
new file mode 100644
index 0000000..9dacf0b
--- /dev/null
+++ b/src/login/pages/FrontchannelLogout.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "frontchannel-logout.ftl" });
+
+const meta = {
+ title: "login/frontchannel-logout.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/IdpReviewUserProfile.stories.tsx b/src/login/pages/IdpReviewUserProfile.stories.tsx
new file mode 100644
index 0000000..6778e77
--- /dev/null
+++ b/src/login/pages/IdpReviewUserProfile.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "idp-review-user-profile.ftl" });
+
+const meta = {
+ title: "login/idp-review-user-profile.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/Info.stories.tsx b/src/login/pages/Info.stories.tsx
new file mode 100644
index 0000000..195061f
--- /dev/null
+++ b/src/login/pages/Info.stories.tsx
@@ -0,0 +1,57 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "info.ftl" });
+
+const meta = {
+ title: "login/info.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ message: {
+ summary: "Server info message"
+ }
+ }}
+ />
+ )
+};
+
+export const WithLinkBack: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ message: {
+ summary: "Server message"
+ },
+ actionUri: undefined
+ }}
+ />
+ )
+};
+
+export const WithRequiredActions: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ message: {
+ summary: "Required actions: "
+ },
+ requiredActions: ["CONFIGURE_TOTP", "UPDATE_PROFILE", "VERIFY_EMAIL", "CUSTOM_ACTION"],
+ "x-keycloakify": {
+ messages: {
+ "requiredAction.CUSTOM_ACTION": "Custom action"
+ }
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/Login.stories.tsx b/src/login/pages/Login.stories.tsx
new file mode 100644
index 0000000..075d1f9
--- /dev/null
+++ b/src/login/pages/Login.stories.tsx
@@ -0,0 +1,233 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login.ftl" });
+
+const meta = {
+ title: "login/login.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
+
+export const WithInvalidCredential: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ login: {
+ username: "johndoe"
+ },
+ messagesPerField: {
+ // NOTE: The other functions of messagesPerField are derived from get() and
+ // existsError() so they are the only ones that need to mock.
+ existsError: (fieldName: string, ...otherFieldNames: string[]) => {
+ const fieldNames = [fieldName, ...otherFieldNames];
+ return fieldNames.includes("username") || fieldNames.includes("password");
+ },
+ get: (fieldName: string) => {
+ if (fieldName === "username" || fieldName === "password") {
+ return "Invalid username or password.";
+ }
+ return "";
+ }
+ }
+ }}
+ />
+ )
+};
+
+export const WithoutRegistration: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ realm: { registrationAllowed: false }
+ }}
+ />
+ )
+};
+
+export const WithoutRememberMe: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ realm: { rememberMe: false }
+ }}
+ />
+ )
+};
+
+export const WithoutPasswordReset: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ realm: { resetPasswordAllowed: false }
+ }}
+ />
+ )
+};
+
+export const WithEmailAsUsername: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ realm: { loginWithEmailAllowed: false }
+ }}
+ />
+ )
+};
+
+export const WithPresetUsername: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ login: { username: "[email protected]" }
+ }}
+ />
+ )
+};
+
+export const WithImmutablePresetUsername: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ auth: {
+ attemptedUsername: "[email protected]",
+ showUsername: true
+ },
+ usernameHidden: true,
+ message: {
+ type: "info",
+ summary: "Please re-authenticate to continue"
+ }
+ }}
+ />
+ )
+};
+
+export const WithSocialProviders: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ social: {
+ displayInfo: true,
+ providers: [
+ {
+ loginUrl: "google",
+ alias: "google",
+ providerId: "google",
+ displayName: "Google",
+ iconClasses: "fa fa-google"
+ },
+ {
+ loginUrl: "microsoft",
+ alias: "microsoft",
+ providerId: "microsoft",
+ displayName: "Microsoft",
+ iconClasses: "fa fa-windows"
+ },
+ {
+ loginUrl: "facebook",
+ alias: "facebook",
+ providerId: "facebook",
+ displayName: "Facebook",
+ iconClasses: "fa fa-facebook"
+ },
+ {
+ loginUrl: "instagram",
+ alias: "instagram",
+ providerId: "instagram",
+ displayName: "Instagram",
+ iconClasses: "fa fa-instagram"
+ },
+ {
+ loginUrl: "twitter",
+ alias: "twitter",
+ providerId: "twitter",
+ displayName: "Twitter",
+ iconClasses: "fa fa-twitter"
+ },
+ {
+ loginUrl: "linkedin",
+ alias: "linkedin",
+ providerId: "linkedin",
+ displayName: "LinkedIn",
+ iconClasses: "fa fa-linkedin"
+ },
+ {
+ loginUrl: "stackoverflow",
+ alias: "stackoverflow",
+ providerId: "stackoverflow",
+ displayName: "Stackoverflow",
+ iconClasses: "fa fa-stack-overflow"
+ },
+ {
+ loginUrl: "github",
+ alias: "github",
+ providerId: "github",
+ displayName: "Github",
+ iconClasses: "fa fa-github"
+ },
+ {
+ loginUrl: "gitlab",
+ alias: "gitlab",
+ providerId: "gitlab",
+ displayName: "Gitlab",
+ iconClasses: "fa fa-gitlab"
+ },
+ {
+ loginUrl: "bitbucket",
+ alias: "bitbucket",
+ providerId: "bitbucket",
+ displayName: "Bitbucket",
+ iconClasses: "fa fa-bitbucket"
+ },
+ {
+ loginUrl: "paypal",
+ alias: "paypal",
+ providerId: "paypal",
+ displayName: "PayPal",
+ iconClasses: "fa fa-paypal"
+ },
+ {
+ loginUrl: "openshift",
+ alias: "openshift",
+ providerId: "openshift",
+ displayName: "OpenShift",
+ iconClasses: "fa fa-cloud"
+ }
+ ]
+ }
+ }}
+ />
+ )
+};
+
+export const WithoutPasswordField: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ realm: { password: false }
+ }}
+ />
+ )
+};
+
+export const WithErrorMessage: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ message: {
+ summary: "The time allotted for the connection has elapsed.<br/>The login process will restart from the beginning.",
+ type: "error"
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/LoginConfigTotp.stories.tsx b/src/login/pages/LoginConfigTotp.stories.tsx
new file mode 100644
index 0000000..5d38df9
--- /dev/null
+++ b/src/login/pages/LoginConfigTotp.stories.tsx
@@ -0,0 +1,43 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-config-totp.ftl" });
+
+const meta = {
+ title: "login/login-config-totp.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
+
+export const WithManualSetUp: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ mode: "manual"
+ }}
+ />
+ )
+};
+
+export const WithError: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ messagesPerField: {
+ get: (fieldName: string) => (fieldName === "totp" ? "Invalid TOTP" : undefined),
+ exists: (fieldName: string) => fieldName === "totp",
+ existsError: (fieldName: string) => fieldName === "totp",
+ printIfExists: <T,>(fieldName: string, x: T) => (fieldName === "totp" ? x : undefined)
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/LoginIdpLinkConfirm.stories.tsx b/src/login/pages/LoginIdpLinkConfirm.stories.tsx
new file mode 100644
index 0000000..6642bf2
--- /dev/null
+++ b/src/login/pages/LoginIdpLinkConfirm.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-idp-link-confirm.ftl" });
+
+const meta = {
+ title: "login/login-idp-link-confirm.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginIdpLinkEmail.stories.tsx b/src/login/pages/LoginIdpLinkEmail.stories.tsx
new file mode 100644
index 0000000..a58ae2d
--- /dev/null
+++ b/src/login/pages/LoginIdpLinkEmail.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-idp-link-email.ftl" });
+
+const meta = {
+ title: "login/login-idp-link-email.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginOauth2DeviceVerifyUserCode.stories.tsx b/src/login/pages/LoginOauth2DeviceVerifyUserCode.stories.tsx
new file mode 100644
index 0000000..c042174
--- /dev/null
+++ b/src/login/pages/LoginOauth2DeviceVerifyUserCode.stories.tsx
@@ -0,0 +1,17 @@
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-oauth2-device-verify-user-code.ftl" });
+
+const meta = {
+ title: "login/login-oauth2-device-verify-user-code.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginOauthGrant.stories.tsx b/src/login/pages/LoginOauthGrant.stories.tsx
new file mode 100644
index 0000000..5356990
--- /dev/null
+++ b/src/login/pages/LoginOauthGrant.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-oauth-grant.ftl" });
+
+const meta = {
+ title: "login/login-oauth-grant.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginOtp.stories.tsx b/src/login/pages/LoginOtp.stories.tsx
new file mode 100644
index 0000000..84b1061
--- /dev/null
+++ b/src/login/pages/LoginOtp.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-otp.ftl" });
+
+const meta = {
+ title: "login/login-otp.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginPageExpired.stories.tsx b/src/login/pages/LoginPageExpired.stories.tsx
new file mode 100644
index 0000000..22e4307
--- /dev/null
+++ b/src/login/pages/LoginPageExpired.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-page-expired.ftl" });
+
+const meta = {
+ title: "login/login-page-expired.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginPassword.stories.tsx b/src/login/pages/LoginPassword.stories.tsx
new file mode 100644
index 0000000..247ec78
--- /dev/null
+++ b/src/login/pages/LoginPassword.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-password.ftl" });
+
+const meta = {
+ title: "login/login-password.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginRecoveryAuthnCodeConfig.stories.tsx b/src/login/pages/LoginRecoveryAuthnCodeConfig.stories.tsx
new file mode 100644
index 0000000..26c181f
--- /dev/null
+++ b/src/login/pages/LoginRecoveryAuthnCodeConfig.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-recovery-authn-code-config.ftl" });
+
+const meta = {
+ title: "login/login-recovery-authn-code-config.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginRecoveryAuthnCodeInput.stories.tsx b/src/login/pages/LoginRecoveryAuthnCodeInput.stories.tsx
new file mode 100644
index 0000000..d4cddcc
--- /dev/null
+++ b/src/login/pages/LoginRecoveryAuthnCodeInput.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-recovery-authn-code-input.ftl" });
+
+const meta = {
+ title: "login/login-recovery-authn-code-input.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginResetOtp.stories.tsx b/src/login/pages/LoginResetOtp.stories.tsx
new file mode 100644
index 0000000..59db763
--- /dev/null
+++ b/src/login/pages/LoginResetOtp.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-reset-otp.ftl" });
+
+const meta = {
+ title: "login/login-reset-otp.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginResetPassword.stories.tsx b/src/login/pages/LoginResetPassword.stories.tsx
new file mode 100644
index 0000000..12412f3
--- /dev/null
+++ b/src/login/pages/LoginResetPassword.stories.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-reset-password.ftl" });
+
+const meta = {
+ title: "login/login-reset-password.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
+
+export const WithEmailAsUsername: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ realm: {
+ loginWithEmailAllowed: true,
+ registrationEmailAsUsername: true
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/LoginUpdatePassword.stories.tsx b/src/login/pages/LoginUpdatePassword.stories.tsx
new file mode 100644
index 0000000..904e398
--- /dev/null
+++ b/src/login/pages/LoginUpdatePassword.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-update-password.ftl" });
+
+const meta = {
+ title: "login/login-update-password.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginUpdateProfile.stories.tsx b/src/login/pages/LoginUpdateProfile.stories.tsx
new file mode 100644
index 0000000..bb5a6e0
--- /dev/null
+++ b/src/login/pages/LoginUpdateProfile.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-update-profile.ftl" });
+
+const meta = {
+ title: "login/login-update-profile.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LoginUsername.stories.tsx b/src/login/pages/LoginUsername.stories.tsx
new file mode 100644
index 0000000..a391ffb
--- /dev/null
+++ b/src/login/pages/LoginUsername.stories.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-username.ftl" });
+
+const meta = {
+ title: "login/login-username.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
+
+export const WithEmailAsUsername: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ realm: {
+ loginWithEmailAllowed: true,
+ registrationEmailAsUsername: true
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/LoginVerifyEmail.stories.tsx b/src/login/pages/LoginVerifyEmail.stories.tsx
new file mode 100644
index 0000000..6da554d
--- /dev/null
+++ b/src/login/pages/LoginVerifyEmail.stories.tsx
@@ -0,0 +1,30 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-verify-email.ftl" });
+
+const meta = {
+ title: "login/login-verify-email.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ message: {
+ summary: "You need to verify your email to activate your account.",
+ type: "warning"
+ },
+ user: {
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/LoginX509Info.stories.tsx b/src/login/pages/LoginX509Info.stories.tsx
new file mode 100644
index 0000000..f3c6be7
--- /dev/null
+++ b/src/login/pages/LoginX509Info.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "login-x509-info.ftl" });
+
+const meta = {
+ title: "login/login-x509-info.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/LogoutConfirm.stories.tsx b/src/login/pages/LogoutConfirm.stories.tsx
new file mode 100644
index 0000000..20ae5c9
--- /dev/null
+++ b/src/login/pages/LogoutConfirm.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "logout-confirm.ftl" });
+
+const meta = {
+ title: "login/logout-confirm.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/Register.stories.tsx b/src/login/pages/Register.stories.tsx
new file mode 100644
index 0000000..0a8c395
--- /dev/null
+++ b/src/login/pages/Register.stories.tsx
@@ -0,0 +1,187 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+import type { Attribute } from "../../../dist/login";
+
+const { KcPageStory } = createKcPageStory({ pageId: "register.ftl" });
+
+const meta = {
+ title: "login/register.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
+
+export const WithEmailAlreadyExists: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ profile: {
+ attributesByName: {
+ username: {
+ value: "johndoe"
+ },
+ email: {
+ },
+ firstName: {
+ value: "John"
+ },
+ lastName: {
+ value: "Doe"
+ }
+ }
+ },
+ messagesPerField: {
+ // NOTE: The other functions of messagesPerField are derived from get() and
+ // existsError() so they are the only ones that need to mock.
+ existsError: (fieldName: string, ...otherFieldNames: string[]) => [fieldName, ...otherFieldNames].includes("email"),
+ get: (fieldName: string) => (fieldName === "email" ? "Email already exists." : undefined)
+ }
+ }}
+ />
+ )
+};
+
+export const WithRestrictedToMITStudents: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ profile: {
+ attributesByName: {
+ email: {
+ validators: {
+ pattern: {
+ pattern: "^[^@]+@([^.]+\\.)*((mit\\.edu)|(berkeley\\.edu))$",
+ "error-message": "${profile.attributes.email.pattern.error}"
+ }
+ },
+ annotations: {
+ inputHelperTextBefore: "${profile.attributes.email.inputHelperTextBefore}"
+ }
+ }
+ }
+ },
+ "x-keycloakify": {
+ messages: {
+ "profile.attributes.email.inputHelperTextBefore": "Please use your MIT or Berkeley email.",
+ "profile.attributes.email.pattern.error":
+ "This is not an MIT (<strong>@mit.edu</strong>) nor a Berkeley (<strong>@berkeley.edu</strong>) email."
+ }
+ }
+ }}
+ />
+ )
+};
+
+export const WithFavoritePet: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ profile: {
+ attributesByName: {
+ favoritePet: {
+ name: "favorite-pet",
+ displayName: "${profile.attributes.favoritePet}",
+ validators: {
+ options: {
+ options: ["cat", "dog", "fish"]
+ }
+ },
+ annotations: {
+ inputOptionLabelsI18nPrefix: "profile.attributes.favoritePet.options"
+ },
+ required: false,
+ readOnly: false
+ } satisfies Attribute
+ }
+ },
+ "x-keycloakify": {
+ messages: {
+ "profile.attributes.favoritePet": "Favorite Pet",
+ "profile.attributes.favoritePet.options.cat": "Fluffy Cat",
+ "profile.attributes.favoritePet.options.dog": "Loyal Dog",
+ "profile.attributes.favoritePet.options.fish": "Peaceful Fish"
+ }
+ }
+ }}
+ />
+ )
+};
+
+export const WithEmailAsUsername: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ realm: {
+ registrationEmailAsUsername: true
+ },
+ profile: {
+ attributesByName: {
+ username: undefined
+ }
+ }
+ }}
+ />
+ )
+};
+
+export const WithRecaptcha: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ scripts: ["https://www.google.com/recaptcha/api.js?hl=en"],
+ recaptchaRequired: true,
+ recaptchaSiteKey: "6LfQHvApAAAAAE73SYTd5vS0lB1Xr7zdiQ-6iBVa"
+ }}
+ />
+ )
+};
+
+export const WithRecaptchaFrench: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ locale: {
+ currentLanguageTag: "fr"
+ },
+ scripts: ["https://www.google.com/recaptcha/api.js?hl=fr"],
+ recaptchaRequired: true,
+ recaptchaSiteKey: "6LfQHvApAAAAAE73SYTd5vS0lB1Xr7zdiQ-6iBVa"
+ }}
+ />
+ )
+};
+
+export const WithPasswordMinLength8: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ passwordPolicies: {
+ length: 8
+ }
+ }}
+ />
+ )
+};
+
+export const WithTermsAcceptance: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ termsAcceptanceRequired: true,
+ "x-keycloakify": {
+ messages: {
+ termsText: "<a href='https://example.com/terms'>Service Terms of Use</a>"
+ }
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/SamlPostForm.stories.tsx b/src/login/pages/SamlPostForm.stories.tsx
new file mode 100644
index 0000000..32faad1
--- /dev/null
+++ b/src/login/pages/SamlPostForm.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "saml-post-form.ftl" });
+
+const meta = {
+ title: "login/saml-post-form.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/SelectAuthenticator.stories.tsx b/src/login/pages/SelectAuthenticator.stories.tsx
new file mode 100644
index 0000000..f5ddcf1
--- /dev/null
+++ b/src/login/pages/SelectAuthenticator.stories.tsx
@@ -0,0 +1,84 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "select-authenticator.ftl" });
+
+const meta = {
+ title: "login/select-authenticator.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
+
+export const WithDifferentAuthenticationMethods: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ auth: {
+ authenticationSelections: [
+ {
+ authExecId: "25697c4e-0c80-4f2c-8eb7-2c16347e8e8d",
+ displayName: "auth-username-password-form-display-name",
+ helpText: "auth-username-password-form-help-text",
+ iconCssClass: "kcAuthenticatorPasswordClass"
+ },
+ {
+ authExecId: "4cb60872-ce0d-4c8f-a806-e651ed77994b",
+ displayName: "webauthn-passwordless-display-name",
+ helpText: "webauthn-passwordless-help-text",
+ iconCssClass: "kcAuthenticatorWebAuthnPasswordlessClass"
+ }
+ ]
+ }
+ }}
+ />
+ )
+};
+
+export const WithRealmTranslations: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ auth: {
+ authenticationSelections: [
+ {
+ authExecId: "f0c22855-eda7-4092-8565-0c22f77d2ffb",
+ displayName: "home-idp-discovery-display-name",
+ helpText: "home-idp-discovery-help-text",
+ iconCssClass: "kcAuthenticatorDefaultClass"
+ },
+ {
+ authExecId: "20456f5a-8b2b-45f3-98e0-551dcb27e3e1",
+ displayName: "identity-provider-redirctor-display-name",
+ helpText: "identity-provider-redirctor-help-text",
+ iconCssClass: "kcAuthenticatorDefaultClass"
+ },
+ {
+ authExecId: "eb435db9-474e-473a-8da7-c184fa510b96",
+ displayName: "auth-username-password-form-display-name",
+ helpText: "auth-username-password-help-text",
+ iconCssClass: "kcAuthenticatorDefaultClass"
+ }
+ ]
+ },
+ "x-keycloakify": {
+ messages: {
+ "home-idp-discovery-display-name": "Home identity provider",
+ "home-idp-discovery-help-text":
+ "Sign in via your home identity provider which will be automatically determined based on your provided email address.",
+ "identity-provider-redirctor-display-name": "Identity Provider Redirector",
+ "identity-provider-redirctor-help-text": "Sign in via your identity provider.",
+ "auth-username-password-help-text": "Sign in via your username and password."
+ }
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/Terms.stories.tsx b/src/login/pages/Terms.stories.tsx
new file mode 100644
index 0000000..f5837b0
--- /dev/null
+++ b/src/login/pages/Terms.stories.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "terms.ftl" });
+
+const meta = {
+ title: "login/terms.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ "x-keycloakify": {
+ messages: {
+ termsText: "<p>My terms in <strong>English</strong></p>"
+ }
+ }
+ }}
+ />
+ )
+};
+
+export const French: Story = {
+ render: () => (
+ <KcPageStory
+ kcContext={{
+ locale: {
+ currentLanguageTag: "fr"
+ },
+ "x-keycloakify": {
+ // cSpell: disable
+ messages: {
+ termsText: "<p>Mes terme en <strong>Français</strong></p>"
+ }
+ // cSpell: enable
+ }
+ }}
+ />
+ )
+};
diff --git a/src/login/pages/UpdateEmail.stories.tsx b/src/login/pages/UpdateEmail.stories.tsx
new file mode 100644
index 0000000..bcb5655
--- /dev/null
+++ b/src/login/pages/UpdateEmail.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "update-email.ftl" });
+
+const meta = {
+ title: "login/update-email.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/WebauthnAuthenticate.stories.tsx b/src/login/pages/WebauthnAuthenticate.stories.tsx
new file mode 100644
index 0000000..fc68e97
--- /dev/null
+++ b/src/login/pages/WebauthnAuthenticate.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "webauthn-authenticate.ftl" });
+
+const meta = {
+ title: "login/webauthn-authenticate.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/WebauthnError.stories.tsx b/src/login/pages/WebauthnError.stories.tsx
new file mode 100644
index 0000000..db76221
--- /dev/null
+++ b/src/login/pages/WebauthnError.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "webauthn-error.ftl" });
+
+const meta = {
+ title: "login/webauthn-error.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};
diff --git a/src/login/pages/WebauthnRegister.stories.tsx b/src/login/pages/WebauthnRegister.stories.tsx
new file mode 100644
index 0000000..ff8b70b
--- /dev/null
+++ b/src/login/pages/WebauthnRegister.stories.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import type { Meta, StoryObj } from "@storybook/react";
+import { createKcPageStory } from "../KcPageStory";
+
+const { KcPageStory } = createKcPageStory({ pageId: "webauthn-register.ftl" });
+
+const meta = {
+ title: "login/webauthn-register.ftl",
+ component: KcPageStory
+} satisfies Meta<typeof KcPageStory>;
+
+export default meta;
+
+type Story = StoryObj<typeof meta>;
+
+export const Default: Story = {
+ render: () => <KcPageStory />
+};