Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/login/KcPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ const Login = lazy(() => import("./pages/Login"));
const Register = lazy(() => import("./pages/Register"));
const LoginUpdateProfile = lazy(() => import("./pages/LoginUpdateProfile"));
const LoginUpdatePassword = lazy(() => import("./pages/LoginUpdatePassword"));

const Error = lazy(() => import("./pages/Error"));
const LoginResetPassword = lazy(() => import("./pages/LoginResetPassword"));
const LoginPageExpired = lazy(() => import("./pages/LoginPageExpired"));
const LoginVerifyEmail = lazy(() => import("./pages/LoginVerifyEmail"));


const doMakeUserConfirmPassword = false;

export default function KcPage(props: { kcContext: KcContext }) {
Expand Down Expand Up @@ -80,6 +83,14 @@ export default function KcPage(props: { kcContext: KcContext }) {
doUseDefaultCss={false}
/>
);
case "error.ftl":
return (
<Error
{...{ kcContext, i18n, classes }}
Template={Template}
doUseDefaultCss={false}
/>
);
case "login-reset-password.ftl":
return (
<LoginResetPassword
Expand Down
64 changes: 64 additions & 0 deletions src/login/pages/Error.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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
kcContext={{
message: {
summary: "An unexpected error occurred. Please try again."
}
}}
/>
)
};

/**
* AccountLocked:
* - Purpose: Tests the error page for account lockout scenarios.
* - Scenario: Simulates a user whose account has been locked due to too many failed login attempts.
* - Key Aspect: Displays a specific error message about account lockout.
*/
export const AccountLocked: Story = {
render: () => (
<KcPageStory
kcContext={{
message: {
summary: "Your account has been temporarily locked due to too many failed login attempts."
}
}}
/>
)
};

/**
* WithClientBaseUrl:
* - Purpose: Tests the error page when a client base URL is available.
* - Scenario: Simulates an error with the option to return to the application.
* - Key Aspect: Displays a "Back to Application" button in addition to "Back to Login".
*/
export const WithClientBaseUrl: Story = {
render: () => (
<KcPageStory
kcContext={{
message: {
summary: "Session expired. Please log in again."
},
client: {
baseUrl: "https://hubee.com"
}
}}
/>
)
};
49 changes: 49 additions & 0 deletions src/login/pages/Error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
import Alert from "@codegouvfr/react-dsfr/Alert";
import { fr } from "@codegouvfr/react-dsfr";

export default function Error(props: PageProps<Extract<KcContext, { pageId: "error.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;

const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});

const { url, message, client } = kcContext;

const { msg } = i18n;

return (
<Template
kcContext={kcContext}
i18n={i18n}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={false}
headerNode={msg("errorTitle")}
>
<Alert
severity="error"
description={message?.summary}
className={fr.cx("fr-mb-4w")}
small
/>

<div className={kcClsx("kcFormGroupClass")} style={{ display: "flex", justifyContent: "flex-end" }}>
{client.baseUrl !== undefined ? (
<a className={fr.cx("fr-link")} href={client.baseUrl}>
{msg("backToApplication")}
</a>
) : (
<a className={fr.cx("fr-link")} href={url.loginUrl}>
{msg("backToLogin")}
</a>
)}
</div>
</Template>
);
}