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
9 changes: 9 additions & 0 deletions src/login/KcPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ 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 Info = lazy(() => import("./pages/Info"));

const Error = lazy(() => import("./pages/Error"));
const LoginResetPassword = lazy(() => import("./pages/LoginResetPassword"));
Expand Down Expand Up @@ -83,6 +84,14 @@ export default function KcPage(props: { kcContext: KcContext }) {
doUseDefaultCss={false}
/>
);
case "info.ftl":
return (
<Info
{...{ kcContext, i18n, classes }}
Template={Template}
doUseDefaultCss={false}
/>
);
case "error.ftl":
return (
<Error
Expand Down
107 changes: 107 additions & 0 deletions src/login/pages/Info.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
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: "Your email has been verified successfully."
},
pageRedirectUri: "https://hubee.com"
}}
/>
)
};

/**
* WithRequiredActions:
* - Purpose: Tests the info page when certain actions are required from the user.
* - Scenario: Simulates a scenario where the user needs to complete specific actions (e.g., verify email, update password).
* - Key Aspect: Displays a list of required actions that the user must complete.
*/
export const WithRequiredActions: Story = {
render: () => (
<KcPageStory
kcContext={{
messageHeader: "Action Required",
message: {
summary: "Please complete the following actions to continue."
},
requiredActions: ["VERIFY_EMAIL", "UPDATE_PASSWORD"],
actionUri: "/auth/realms/hubee/login-actions/required-action"
}}
/>
)
};

/**
* WithActionUri:
* - Purpose: Tests the page with an action URI link.
* - Scenario: Simulates an info page that provides a link to proceed with a specific action.
* - Key Aspect: Displays a "proceed with action" link instead of "back to application".
*/
export const WithActionUri: Story = {
render: () => (
<KcPageStory
kcContext={{
message: {
summary: "Your profile has been updated."
},
actionUri: "/auth/realms/hubee/account"
}}
/>
)
};

/**
* WithClientBaseUrl:
* - Purpose: Tests the page with only a client base URL available.
* - Scenario: Simulates when no specific redirect URI is provided, falling back to the client's base URL.
* - Key Aspect: Uses the client base URL as the "back to application" link.
*/
export const WithClientBaseUrl: Story = {
render: () => (
<KcPageStory
kcContext={{
message: {
summary: "Your settings have been saved."
},
client: {
baseUrl: "https://hubee.com"
}
}}
/>
)
};

/**
* EmailVerificationSuccess:
* - Purpose: Tests a typical email verification success message.
* - Scenario: User clicks on email verification link and sees success message.
* - Key Aspect: Shows success message with redirect to application.
*/
export const EmailVerificationSuccess: Story = {
render: () => (
<KcPageStory
kcContext={{
messageHeader: "Email verified",
message: {
summary: "Your email address has been verified successfully."
},
pageRedirectUri: "https://hubee.com/dashboard"
}}
/>
)
};
71 changes: 71 additions & 0 deletions src/login/pages/Info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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 Info(props: PageProps<Extract<KcContext, { pageId: "info.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;

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

const { messageHeader, message, requiredActions, skipLink, pageRedirectUri, actionUri, client, url } = kcContext;

const { msg, advancedMsg } = i18n;

return (
<Template
kcContext={kcContext}
i18n={i18n}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={false}
headerNode={messageHeader !== undefined ? advancedMsg(messageHeader) : undefined}
>
{message !== undefined && (
<Alert
severity="success"
description={advancedMsg(message.summary)}
className={fr.cx("fr-mb-4w")}
small
/>
)}

{requiredActions !== undefined && requiredActions.length > 0 && (
<div className={kcClsx("kcFormGroupClass")}>
<ul className={fr.cx("fr-mb-2w")}>
{requiredActions.map((requiredAction, index) => (
<li key={index}>{advancedMsg(`requiredAction.${requiredAction}` as any)}</li>
))}
</ul>
</div>
)}

<div className={kcClsx("kcFormGroupClass")} style={{ display: "flex", justifyContent: "flex-end" }}>
{!skipLink && (
actionUri !== undefined ? (
<a className={fr.cx("fr-link", "fr-link--icon-right", "fr-icon-arrow-right-line")} href={actionUri}>
{msg("doContinue")}
</a>
) : pageRedirectUri !== undefined ? (
<a className={fr.cx("fr-link")} href={pageRedirectUri}>
{msg("backToApplication")}
</a>
) : 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>
);
}