aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/keycloak-theme/KcApp.tsx48
-rw-r--r--src/keycloak-theme/Template.tsx103
-rw-r--r--src/keycloak-theme/i18n.ts50
-rw-r--r--src/keycloak-theme/kcContext.ts2
-rw-r--r--src/keycloak-theme/pages/Login.tsx370
-rw-r--r--src/keycloak-theme/pages/MyExtraPage1.tsx8
-rw-r--r--src/keycloak-theme/pages/MyExtraPage2.tsx6
-rw-r--r--src/keycloak-theme/pages/Register.tsx132
-rw-r--r--src/keycloak-theme/pages/RegisterUserProfile.tsx46
-rw-r--r--src/keycloak-theme/pages/Terms.tsx68
10 files changed, 422 insertions, 411 deletions
diff --git a/src/keycloak-theme/KcApp.tsx b/src/keycloak-theme/KcApp.tsx
index 1235b6b..81d83ca 100644
--- a/src/keycloak-theme/KcApp.tsx
+++ b/src/keycloak-theme/KcApp.tsx
@@ -2,31 +2,29 @@ import "./KcApp.css";
import { lazy, Suspense } from "react";
import type { KcContext } from "./kcContext";
import { useI18n } from "./i18n";
-import Fallback, { defaultKcProps, type KcProps, type PageProps } from "keycloakify";
-import Template from "./Template";
-import DefaultTemplate from "keycloakify/lib/Template";
+import Fallback, { type PageProps } from "keycloakify";
+
+const Template = lazy(() => import("./Template"));
+const DefaultTemplate = lazy(() => import("keycloakify/Template"));
// You can uncomment this to see the values passed by the main app before redirecting.
//import { foo, bar } from "./valuesTransferredOverUrl";
//console.log(`Values passed by the main app in the URL parameter:`, { foo, bar });
-const Login = lazy(()=> import("./pages/Login"));
+const Login = lazy(() => import("./pages/Login"));
// If you can, favor register-user-profile.ftl over register.ftl, see: https://docs.keycloakify.dev/realtime-input-validation
const Register = lazy(() => import("./pages/Register"));
const RegisterUserProfile = lazy(() => import("./pages/RegisterUserProfile"));
const Terms = lazy(() => import("./pages/Terms"));
const MyExtraPage1 = lazy(() => import("./pages/MyExtraPage1"));
const MyExtraPage2 = lazy(() => import("./pages/MyExtraPage2"));
-const Info = lazy(()=> import("keycloakify/lib/pages/Info"));
+const Info = lazy(() => import("keycloakify/pages/Info"));
-// This is like editing the theme.properties
+// This is like adding classes to theme.properties
// https://github.com/keycloak/keycloak/blob/11.0.3/themes/src/main/resources/theme/keycloak/login/theme.properties
-const kcProps: KcProps = {
- ...defaultKcProps,
+const classes: PageProps<any, any>["classes"] = {
// NOTE: The classes are defined in ./KcApp.css
- // You can add your classes alongside thoses that are present in the default Keycloak theme...
- "kcHtmlClass": [...defaultKcProps.kcHtmlClass, "my-root-class"],
- // ...or overwrite
+ "kcHtmlClass": "my-root-class",
"kcHeaderWrapperClass": "my-color my-font"
};
@@ -40,36 +38,26 @@ export default function App(props: { kcContext: KcContext; }) {
//NOTE: Locales not yet downloaded, we could as well display a loading progress but it's usually a matter of milliseconds.
return null;
}
-
+
/*
* Examples assuming i18n.currentLanguageTag === "en":
* i18n.msg("access-denied") === <span>Access denied</span>
* i18n.msg("foo") === <span>foo in English</span>
*/
- const pageProps: Omit<PageProps<any, typeof i18n>, "kcContext"> = {
- i18n,
- // Here we have overloaded the default template, however you could use the default one with:
- //Template: DefaultTemplate,
- Template,
- // Wether or not we should download the CSS and JS resources that comes with the default Keycloak theme.
- doFetchDefaultThemeResources: true,
- ...kcProps,
- };
-
return (
<Suspense>
{(() => {
switch (kcContext.pageId) {
- case "login.ftl": return <Login {...{ kcContext, ...pageProps }} />;
- case "register.ftl": return <Register {...{ kcContext, ...pageProps }} />;
- case "register-user-profile.ftl": return <RegisterUserProfile {...{ kcContext, ...pageProps }} />
- case "terms.ftl": return <Terms {...{ kcContext, ...pageProps }} />;
- case "my-extra-page-1.ftl": return <MyExtraPage1 {...{ kcContext, ...pageProps }} />;
- case "my-extra-page-2.ftl": return <MyExtraPage2 {...{ kcContext, ...pageProps }} />;
+ case "login.ftl": return <Login {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
+ case "register.ftl": return <Register {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
+ case "register-user-profile.ftl": return <RegisterUserProfile {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />
+ case "terms.ftl": return <Terms {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
+ case "my-extra-page-1.ftl": return <MyExtraPage1 {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
+ case "my-extra-page-2.ftl": return <MyExtraPage2 {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
// We choose to use the default Template for the Info page and to download the theme resources.
- case "info.ftl": return <Info {...{ kcContext, ...pageProps}} Template={DefaultTemplate} doFetchDefaultThemeResources={true} />;
- default: return <Fallback {...{ kcContext, ...pageProps }} />;
+ case "info.ftl": return <Info {...{ kcContext, i18n, "Template": DefaultTemplate, classes, "doUseDefaultCss": true }} />;
+ default: return <Fallback {...{ kcContext, i18n, "Template": DefaultTemplate, classes, "doUseDefaultCss": true }} />;
}
})()}
</Suspense>
diff --git a/src/keycloak-theme/Template.tsx b/src/keycloak-theme/Template.tsx
index bd4e46a..57a1566 100644
--- a/src/keycloak-theme/Template.tsx
+++ b/src/keycloak-theme/Template.tsx
@@ -1,13 +1,9 @@
-// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/components/shared/Template.tsx
-
-// You can replace all relative imports by cherry picking files from the keycloakify module.
-// For example, the following import:
-// import { assert } from "./tools/assert";
-// becomes:
-import { assert } from "keycloakify/lib/tools/assert";
-import { clsx } from "keycloakify/lib/tools/clsx";
-import type { TemplateProps } from "keycloakify/lib/KcProps";
-import { usePrepareTemplate } from "keycloakify/lib/Template";
+// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/Template.tsx
+import { assert } from "keycloakify/tools/assert";
+import { clsx } from "keycloakify/tools/clsx";
+import { usePrepareTemplate } from "keycloakify/lib/usePrepareTemplate";
+import { type TemplateProps, defaultTemplateClasses } from "keycloakify/TemplateProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
import type { KcContext } from "./kcContext";
import type { I18n } from "./i18n";
@@ -24,24 +20,29 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
infoNode = null,
kcContext,
i18n,
- doFetchDefaultThemeResources,
- stylesCommon,
- styles,
- scripts,
- kcHtmlClass
+ doUseDefaultCss,
+ classes
} = props;
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultTemplateClasses,
+ classes
+ });
+
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext;
const { isReady } = usePrepareTemplate({
- doFetchDefaultThemeResources,
- stylesCommon,
- styles,
- scripts,
+ "doFetchDefaultThemeResources": doUseDefaultCss,
url,
- kcHtmlClass
+ "stylesCommon": [
+ "node_modules/patternfly/dist/css/patternfly.min.css",
+ "node_modules/patternfly/dist/css/patternfly-additions.min.css",
+ "lib/zocial/zocial.css"
+ ],
+ "styles": ["css/login.css"],
+ "htmlClassName": getClassName("kcHtmlClass")
});
if (!isReady) {
@@ -49,18 +50,18 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
}
return (
- <div className={clsx(props.kcLoginClass)}>
- <div id="kc-header" className={clsx(props.kcHeaderClass)}>
- <div id="kc-header-wrapper" className={clsx(props.kcHeaderWrapperClass)}>
+ <div className={getClassName("kcLoginClass")}>
+ <div id="kc-header" className={getClassName("kcHeaderClass")}>
+ <div id="kc-header-wrapper" className={getClassName("kcHeaderWrapperClass")}>
{msg("loginTitleHtml", realm.displayNameHtml)}
</div>
</div>
- <div className={clsx(props.kcFormCardClass, displayWide && props.kcFormCardAccountClass)}>
- <header className={clsx(props.kcFormHeaderClass)}>
+ <div className={clsx(getClassName("kcFormCardClass"), displayWide && getClassName("kcFormCardAccountClass"))}>
+ <header className={getClassName("kcFormHeaderClass")}>
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
<div id="kc-locale">
- <div id="kc-locale-wrapper" className={clsx(props.kcLocaleWrapperClass)}>
+ <div id="kc-locale-wrapper" className={getClassName("kcLocaleWrapperClass")}>
<div className="kc-dropdown" id="kc-locale-dropdown">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" id="kc-current-locale-link">
@@ -82,8 +83,8 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
)}
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
displayRequiredFields ? (
- <div className={clsx(props.kcContentWrapperClass)}>
- <div className={clsx(props.kcLabelWrapperClass, "subtitle")}>
+ <div className={getClassName("kcContentWrapperClass")}>
+ <div className={clsx(getClassName("kcLabelWrapperClass"), "subtitle")}>
<span className="subtitle">
<span className="required">*</span>
{msg("requiredFields")}
@@ -97,20 +98,20 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
<h1 id="kc-page-title">{headerNode}</h1>
)
) : displayRequiredFields ? (
- <div className={clsx(props.kcContentWrapperClass)}>
- <div className={clsx(props.kcLabelWrapperClass, "subtitle")}>
+ <div className={getClassName("kcContentWrapperClass")}>
+ <div className={clsx(getClassName("kcLabelWrapperClass"), "subtitle")}>
<span className="subtitle">
<span className="required">*</span> {msg("requiredFields")}
</span>
</div>
<div className="col-md-10">
{showUsernameNode}
- <div className={clsx(props.kcFormGroupClass)}>
+ <div className={getClassName("kcFormGroupClass")}>
<div id="kc-username">
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl}>
<div className="kc-login-tooltip">
- <i className={clsx(props.kcResetFlowIcon)}></i>
+ <i className={getClassName("kcResetFlowIcon")}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
@@ -121,12 +122,12 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
) : (
<>
{showUsernameNode}
- <div className={clsx(props.kcFormGroupClass)}>
+ <div className={getClassName("kcFormGroupClass")}>
<div id="kc-username">
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl}>
<div className="kc-login-tooltip">
- <i className={clsx(props.kcResetFlowIcon)}></i>
+ <i className={getClassName("kcResetFlowIcon")}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
@@ -140,10 +141,10 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
{/* App-initiated actions should not see warning messages about the need to complete the action during login. */}
{displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
<div className={clsx("alert", `alert-${message.type}`)}>
- {message.type === "success" && <span className={clsx(props.kcFeedbackSuccessIcon)}></span>}
- {message.type === "warning" && <span className={clsx(props.kcFeedbackWarningIcon)}></span>}
- {message.type === "error" && <span className={clsx(props.kcFeedbackErrorIcon)}></span>}
- {message.type === "info" && <span className={clsx(props.kcFeedbackInfoIcon)}></span>}
+ {message.type === "success" && <span className={getClassName("kcFeedbackSuccessIcon")}></span>}
+ {message.type === "warning" && <span className={getClassName("kcFeedbackWarningIcon")}></span>}
+ {message.type === "error" && <span className={getClassName("kcFeedbackErrorIcon")}></span>}
+ {message.type === "info" && <span className={getClassName("kcFeedbackInfoIcon")}></span>}
<span
className="kc-feedback-text"
dangerouslySetInnerHTML={{
@@ -158,16 +159,24 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
id="kc-select-try-another-way-form"
action={url.loginAction}
method="post"
- className={clsx(displayWide && props.kcContentWrapperClass)}
+ className={clsx(displayWide && getClassName("kcContentWrapperClass"))}
>
- <div className={clsx(displayWide && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}>
- <div className={clsx(props.kcFormGroupClass)}>
+ <div
+ className={clsx(
+ displayWide && [getClassName("kcFormSocialAccountContentClass"), getClassName("kcFormSocialAccountClass")]
+ )}
+ >
+ <div className={getClassName("kcFormGroupClass")}>
<input type="hidden" name="tryAnotherWay" value="on" />
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
- <a href="#" id="try-another-way" onClick={() => {
- document.forms["kc-select-try-another-way-form" as never].submit();
- return false;
- }}>
+ <a
+ href="#"
+ id="try-another-way"
+ onClick={() => {
+ document.forms["kc-select-try-another-way-form" as never].submit();
+ return false;
+ }}
+ >
{msg("doTryAnotherWay")}
</a>
</div>
@@ -175,8 +184,8 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
</form>
)}
{displayInfo && (
- <div id="kc-info" className={clsx(props.kcSignUpClass)}>
- <div id="kc-info-wrapper" className={clsx(props.kcInfoAreaWrapperClass)}>
+ <div id="kc-info" className={getClassName("kcSignUpClass")}>
+ <div id="kc-info-wrapper" className={getClassName("kcInfoAreaWrapperClass")}>
{infoNode}
</div>
</div>
diff --git a/src/keycloak-theme/i18n.ts b/src/keycloak-theme/i18n.ts
index 3b89a1e..bbeab0b 100644
--- a/src/keycloak-theme/i18n.ts
+++ b/src/keycloak-theme/i18n.ts
@@ -1,32 +1,24 @@
-import { useI18n as useI18nBase } from "keycloakify";
+import { createUseI18n } from "keycloakify";
-type Props = Omit<Parameters<typeof useI18nBase>[0], "extraMessages">;
-
-export function useI18n(props: Props) {
- const { kcContext } = props;
- return useI18nBase({
- kcContext,
- // NOTE: Here you can override the default i18n messages
- // or define new ones that, for example, you would have
- // defined in the Keycloak admin UI for UserProfile
- // https://user-images.githubusercontent.com/6702424/182050652-522b6fe6-8ee5-49df-aca3-dba2d33f24a5.png
- "extraMessages": {
- "en": {
- "alphanumericalCharsOnly": "Only alphanumerical characters",
- "gender": "Gender",
- // Here we overwrite the default english value for the message "doForgotPassword"
- // that is "Forgot Password?" see: https://github.com/InseeFrLab/keycloakify/blob/f0ae5ea908e0aa42391af323b6d5e2fd371af851/src/lib/i18n/generated_messages/18.0.1/login/en.ts#L17
- "doForgotPassword": "I forgot my password",
- },
- "fr": {
- /* spell-checker: disable */
- "alphanumericalCharsOnly": "Caractère alphanumérique uniquement",
- "gender": "Genre",
- "doForgotPassword": "J'ai oublié mon mot de passe"
- /* spell-checker: enable */
- },
- },
- });
-}
+export const { useI18n } = createUseI18n({
+ // NOTE: Here you can override the default i18n messages
+ // or define new ones that, for example, you would have
+ // defined in the Keycloak admin UI for UserProfile
+ // https://user-images.githubusercontent.com/6702424/182050652-522b6fe6-8ee5-49df-aca3-dba2d33f24a5.png
+ en: {
+ alphanumericalCharsOnly: "Only alphanumerical characters",
+ gender: "Gender",
+ // Here we overwrite the default english value for the message "doForgotPassword"
+ // that is "Forgot Password?" see: https://github.com/InseeFrLab/keycloakify/blob/f0ae5ea908e0aa42391af323b6d5e2fd371af851/src/lib/i18n/generated_messages/18.0.1/login/en.ts#L17
+ doForgotPassword: "I forgot my password",
+ },
+ fr: {
+ /* spell-checker: disable */
+ alphanumericalCharsOnly: "Caractère alphanumérique uniquement",
+ gender: "Genre",
+ doForgotPassword: "J'ai oublié mon mot de passe"
+ /* spell-checker: enable */
+ }
+});
export type I18n = NonNullable<ReturnType<typeof useI18n>>;
diff --git a/src/keycloak-theme/kcContext.ts b/src/keycloak-theme/kcContext.ts
index 078efeb..44b8069 100644
--- a/src/keycloak-theme/kcContext.ts
+++ b/src/keycloak-theme/kcContext.ts
@@ -1,4 +1,4 @@
-import { getKcContext } from "keycloakify/lib/getKcContext";
+import { getKcContext } from "keycloakify";
export type KcContextExtension =
// NOTE: A 'keycloakify' field must be added
diff --git a/src/keycloak-theme/pages/Login.tsx b/src/keycloak-theme/pages/Login.tsx
index 1c1559a..932f1ae 100644
--- a/src/keycloak-theme/pages/Login.tsx
+++ b/src/keycloak-theme/pages/Login.tsx
@@ -1,198 +1,204 @@
+// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/pages/Login.tsx
+
import { useState, type FormEventHandler } from "react";
-// This is a copy paste from https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/pages/Login.tsx
-// You can replace all relative imports by cherry picking files from the keycloakify module.
-// For example, the following import:
-// import { clsx } from "./tools/clsx";
-// becomes:
-import { clsx } from "keycloakify/lib/tools/clsx";
-import { useConstCallback } from "keycloakify/lib/tools/useConstCallback";
-import type { PageProps } from "keycloakify/lib/KcProps";
-// Here use your own KcContext and I18n that you might have overloaded.
+import { clsx } from "keycloakify/tools/clsx";
+import { useConstCallback } from "keycloakify/tools/useConstCallback";
+import { type PageProps, defaultClasses } from "keycloakify/pages/PageProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
-export default function Login(props: PageProps<Extract<KcContext, { pageId: "login.ftl"; }>, I18n>) {
- const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props;
+export default function Login(props: PageProps<Extract<KcContext, { pageId: "login.ftl" }>, I18n>) {
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
+
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
+ classes
+ });
- const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
+ const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
- const { msg, msgStr } = i18n;
+ const { msg, msgStr } = i18n;
- const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
+ const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
- const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
- e.preventDefault();
+ const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
+ e.preventDefault();
- setIsLoginButtonDisabled(true);
+ setIsLoginButtonDisabled(true);
- const formElement = e.target as HTMLFormElement;
+ const formElement = e.target as HTMLFormElement;
- //NOTE: Even if we login with email Keycloak expect username and password in
- //the POST request.
- formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
+ //NOTE: Even if we login with email Keycloak expect username and password in
+ //the POST request.
+ formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
- formElement.submit();
- });
+ formElement.submit();
+ });
- return (
- <Template
- {...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
- displayInfo={social.displayInfo}
- displayWide={realm.password && social.providers !== undefined}
- headerNode={msg("doLogIn")}
- formNode={
- <div id="kc-form" className={clsx(realm.password && social.providers !== undefined && kcProps.kcContentWrapperClass)}>
- <div
- id="kc-form-wrapper"
- className={clsx(
- realm.password && social.providers && [kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass]
- )}
- >
- {realm.password && (
- <form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
- <div className={clsx(kcProps.kcFormGroupClass)}>
- {(() => {
- const label = !realm.loginWithEmailAllowed
- ? "username"
- : realm.registrationEmailAsUsername
- ? "email"
- : "usernameOrEmail";
+ return (
+ <Template
+ {...{ kcContext, i18n, doUseDefaultCss, classes }}
+ displayInfo={social.displayInfo}
+ displayWide={realm.password && social.providers !== undefined}
+ headerNode={msg("doLogIn")}
+ formNode={
+ <div id="kc-form" className={clsx(realm.password && social.providers !== undefined && getClassName("kcContentWrapperClass"))}>
+ <div
+ id="kc-form-wrapper"
+ className={clsx(
+ realm.password &&
+ social.providers && [getClassName("kcFormSocialAccountContentClass"), getClassName("kcFormSocialAccountClass")]
+ )}
+ >
+ {realm.password && (
+ <form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
+ <div className={getClassName("kcFormGroupClass")}>
+ {(() => {
+ const label = !realm.loginWithEmailAllowed
+ ? "username"
+ : realm.registrationEmailAsUsername
+ ? "email"
+ : "usernameOrEmail";
- const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
+ const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
- return (
- <>
- <label htmlFor={autoCompleteHelper} className={clsx(kcProps.kcLabelClass)}>
- {msg(label)}
- </label>
- <input
- tabIndex={1}
- id={autoCompleteHelper}
- className={clsx(kcProps.kcInputClass)}
- //NOTE: This is used by Google Chrome auto fill so we use it to tell
- //the browser how to pre fill the form but before submit we put it back
- //to username because it is what keycloak expects.
- name={autoCompleteHelper}
- defaultValue={login.username ?? ""}
- type="text"
- {...(usernameEditDisabled
- ? { "disabled": true }
- : {
- "autoFocus": true,
- "autoComplete": "off"
- })}
- />
- </>
- );
- })()}
- </div>
- <div className={clsx(kcProps.kcFormGroupClass)}>
- <label htmlFor="password" className={clsx(kcProps.kcLabelClass)}>
- {msg("password")}
- </label>
- <input
- tabIndex={2}
- id="password"
- className={clsx(kcProps.kcInputClass)}
- name="password"
- type="password"
- autoComplete="off"
- />
- </div>
- <div className={clsx(kcProps.kcFormGroupClass, kcProps.kcFormSettingClass)}>
- <div id="kc-form-options">
- {realm.rememberMe && !usernameEditDisabled && (
- <div className="checkbox">
- <label>
- <input
- tabIndex={3}
- id="rememberMe"
- name="rememberMe"
- type="checkbox"
- {...(login.rememberMe
- ? {
- "checked": true
- }
- : {})}
- />
- {msg("rememberMe")}
- </label>
- </div>
- )}
- </div>
- <div className={clsx(kcProps.kcFormOptionsWrapperClass)}>
- {realm.resetPasswordAllowed && (
- <span>
- <a tabIndex={5} href={url.loginResetCredentialsUrl}>
- {msg("doForgotPassword")}
- </a>
- </span>
- )}
- </div>
- </div>
- <div id="kc-form-buttons" className={clsx(kcProps.kcFormGroupClass)}>
- <input
- type="hidden"
- id="id-hidden-input"
- name="credentialId"
- {...(auth?.selectedCredential !== undefined
- ? {
- "value": auth.selectedCredential
- }
- : {})}
- />
- <input
- tabIndex={4}
- className={clsx(
- kcProps.kcButtonClass,
- kcProps.kcButtonPrimaryClass,
- kcProps.kcButtonBlockClass,
- kcProps.kcButtonLargeClass
- )}
- name="login"
- id="kc-login"
- type="submit"
- value={msgStr("doLogIn")}
- disabled={isLoginButtonDisabled}
- />
- </div>
- </form>
- )}
- </div>
- {realm.password && social.providers !== undefined && (
- <div id="kc-social-providers" className={clsx(kcProps.kcFormSocialAccountContentClass, kcProps.kcFormSocialAccountClass)}>
- <ul
- className={clsx(
- kcProps.kcFormSocialAccountListClass,
- social.providers.length > 4 && kcProps.kcFormSocialAccountDoubleListClass
- )}
- >
- {social.providers.map(p => (
- <li key={p.providerId} className={clsx(kcProps.kcFormSocialAccountListLinkClass)}>
- <a href={p.loginUrl} id={`zocial-${p.alias}`} className={clsx("zocial", p.providerId)}>
- <span>{p.displayName}</span>
- </a>
- </li>
- ))}
- </ul>
- </div>
- )}
- </div>
- }
- infoNode={
- realm.password &&
- realm.registrationAllowed &&
- !registrationDisabled && (
- <div id="kc-registration">
- <span>
- {msg("noAccount")}
- <a tabIndex={6} href={url.registrationUrl}>
- {msg("doRegister")}
- </a>
- </span>
- </div>
- )
- }
- />
- );
+ return (
+ <>
+ <label htmlFor={autoCompleteHelper} className={getClassName("kcLabelClass")}>
+ {msg(label)}
+ </label>
+ <input
+ tabIndex={1}
+ id={autoCompleteHelper}
+ className={getClassName("kcInputClass")}
+ //NOTE: This is used by Google Chrome auto fill so we use it to tell
+ //the browser how to pre fill the form but before submit we put it back
+ //to username because it is what keycloak expects.
+ name={autoCompleteHelper}
+ defaultValue={login.username ?? ""}
+ type="text"
+ {...(usernameEditDisabled
+ ? { "disabled": true }
+ : {
+ "autoFocus": true,
+ "autoComplete": "off"
+ })}
+ />
+ </>
+ );
+ })()}
+ </div>
+ <div className={getClassName("kcFormGroupClass")}>
+ <label htmlFor="password" className={getClassName("kcLabelClass")}>
+ {msg("password")}
+ </label>
+ <input
+ tabIndex={2}
+ id="password"
+ className={getClassName("kcInputClass")}
+ name="password"
+ type="password"
+ autoComplete="off"
+ />
+ </div>
+ <div className={clsx(getClassName("kcFormGroupClass"), getClassName("kcFormSettingClass"))}>
+ <div id="kc-form-options">
+ {realm.rememberMe && !usernameEditDisabled && (
+ <div className="checkbox">
+ <label>
+ <input
+ tabIndex={3}
+ id="rememberMe"
+ name="rememberMe"
+ type="checkbox"
+ {...(login.rememberMe
+ ? {
+ "checked": true
+ }
+ : {})}
+ />
+ {msg("rememberMe")}
+ </label>
+ </div>
+ )}
+ </div>
+ <div className={getClassName("kcFormOptionsWrapperClass")}>
+ {realm.resetPasswordAllowed && (
+ <span>
+ <a tabIndex={5} href={url.loginResetCredentialsUrl}>
+ {msg("doForgotPassword")}
+ </a>
+ </span>
+ )}
+ </div>
+ </div>
+ <div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
+ <input
+ type="hidden"
+ id="id-hidden-input"
+ name="credentialId"
+ {...(auth?.selectedCredential !== undefined
+ ? {
+ "value": auth.selectedCredential
+ }
+ : {})}
+ />
+ <input
+ tabIndex={4}
+ className={clsx(
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonPrimaryClass"),
+ getClassName("kcButtonBlockClass"),
+ getClassName("kcButtonLargeClass")
+ )}
+ name="login"
+ id="kc-login"
+ type="submit"
+ value={msgStr("doLogIn")}
+ disabled={isLoginButtonDisabled}
+ />
+ </div>
+ </form>
+ )}
+ </div>
+ {realm.password && social.providers !== undefined && (
+ <div
+ id="kc-social-providers"
+ className={clsx(getClassName("kcFormSocialAccountContentClass"), getClassName("kcFormSocialAccountClass"))}
+ >
+ <ul
+ className={clsx(
+ getClassName("kcFormSocialAccountListClass"),
+ social.providers.length > 4 && getClassName("kcFormSocialAccountDoubleListClass")
+ )}
+ >
+ {social.providers.map(p => (
+ <li key={p.providerId} className={getClassName("kcFormSocialAccountListLinkClass")}>
+ <a href={p.loginUrl} id={`zocial-${p.alias}`} className={clsx("zocial", p.providerId)}>
+ <span>{p.displayName}</span>
+ </a>
+ </li>
+ ))}
+ </ul>
+ </div>
+ )}
+ </div>
+ }
+ infoNode={
+ realm.password &&
+ realm.registrationAllowed &&
+ !registrationDisabled && (
+ <div id="kc-registration">
+ <span>
+ {msg("noAccount")}
+ <a tabIndex={6} href={url.registrationUrl}>
+ {msg("doRegister")}
+ </a>
+ </span>
+ </div>
+ )
+ }
+ />
+ );
}
diff --git a/src/keycloak-theme/pages/MyExtraPage1.tsx b/src/keycloak-theme/pages/MyExtraPage1.tsx
index 82d54d1..ed82c60 100644
--- a/src/keycloak-theme/pages/MyExtraPage1.tsx
+++ b/src/keycloak-theme/pages/MyExtraPage1.tsx
@@ -1,21 +1,21 @@
-import type { PageProps } from "keycloakify";
+import type { PageProps } from "keycloakify/pages/PageProps";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
export default function MyExtraPage1(props: PageProps<Extract<KcContext, { pageId: "my-extra-page-1.ftl"; }>, I18n>) {
- const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props;
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
return (
<Template
- {...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
+ {...{ kcContext, i18n, doUseDefaultCss, classes }}
headerNode={<>Header <i>text</i></>}
formNode={
<form>
{/*...*/}
</form>
}
- infoNode={<span>footer</span> }
+ infoNode={<span>footer</span>}
/>
);
diff --git a/src/keycloak-theme/pages/MyExtraPage2.tsx b/src/keycloak-theme/pages/MyExtraPage2.tsx
index a0d00f8..e086f99 100644
--- a/src/keycloak-theme/pages/MyExtraPage2.tsx
+++ b/src/keycloak-theme/pages/MyExtraPage2.tsx
@@ -1,17 +1,17 @@
-import type { PageProps } from "keycloakify";
+import type { PageProps } from "keycloakify/pages/PageProps";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
export default function MyExtraPage1(props: PageProps<Extract<KcContext, { pageId: "my-extra-page-2.ftl"; }>, I18n>) {
- const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props;
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
// someCustomValue is declared by you in ../kcContext.ts
console.log(`TODO: Do something with: ${kcContext.someCustomValue}`);
return (
<Template
- {...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
+ {...{ kcContext, i18n, doUseDefaultCss, classes }}
headerNode={<>Header <i>text</i></>}
formNode={
<form>
diff --git a/src/keycloak-theme/pages/Register.tsx b/src/keycloak-theme/pages/Register.tsx
index dd19bba..666d8b8 100644
--- a/src/keycloak-theme/pages/Register.tsx
+++ b/src/keycloak-theme/pages/Register.tsx
@@ -1,74 +1,89 @@
-// This is a copy paste from https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/pages/Register.tsx
-// It is now up to us to implement a special behavior to leverage the non standard authorizedMailDomains
-// provided by the plugin: https://github.com/micedre/keycloak-mail-whitelisting installed on our keycloak server.
-// Note that it is no longer recommended to use register.ftl, it's best to use register-user-profile.ftl
-// See: https://docs.keycloakify.dev/realtime-input-validation
-import { clsx } from "keycloakify/lib/tools/clsx";
-import type { PageProps } from "keycloakify/lib/KcProps";
+// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/pages/Register.tsx
+
+import { clsx } from "keycloakify/tools/clsx";
+import { type PageProps, defaultClasses } from "keycloakify/pages/PageProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
-export default function Register(props: PageProps<Extract<KcContext, { pageId: "register.ftl"; }>, I18n>) {
- const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props;
+export default function Register(props: PageProps<Extract<KcContext, { pageId: "register.ftl" }>, I18n>) {
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
+
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
+ classes
+ });
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
const { msg, msgStr } = i18n;
- console.log(`NOTE: It is up to you do do something meaningful with ${kcContext.authorizedMailDomains}`);
-
return (
<Template
- {...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
+ {...{ kcContext, i18n, doUseDefaultCss, classes }}
headerNode={msg("registerTitle")}
formNode={
- <form id="kc-register-form" className={clsx(kcProps.kcFormClass)} action={url.registrationAction} method="post">
- <div className={clsx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("firstName", kcProps.kcFormGroupErrorClass))}>
- <div className={clsx(kcProps.kcLabelWrapperClass)}>
- <label htmlFor="firstName" className={clsx(kcProps.kcLabelClass)}>
+ <form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
+ <div
+ className={clsx(
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("firstName", getClassName("kcFormGroupErrorClass"))
+ )}
+ >
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="firstName" className={getClassName("kcLabelClass")}>
{msg("firstName")}
</label>
</div>
- <div className={clsx(kcProps.kcInputWrapperClass)}>
+ <div className={getClassName("kcInputWrapperClass")}>
<input
type="text"
id="firstName"
- className={clsx(kcProps.kcInputClass)}
+ className={getClassName("kcInputClass")}
name="firstName"
defaultValue={register.formData.firstName ?? ""}
/>
</div>
</div>
- <div className={clsx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("lastName", kcProps.kcFormGroupErrorClass))}>
- <div className={clsx(kcProps.kcLabelWrapperClass)}>
- <label htmlFor="lastName" className={clsx(kcProps.kcLabelClass)}>
+ <div
+ className={clsx(
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("lastName", getClassName("kcFormGroupErrorClass"))
+ )}
+ >
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="lastName" className={getClassName("kcLabelClass")}>
{msg("lastName")}
</label>
</div>
- <div className={clsx(kcProps.kcInputWrapperClass)}>
+ <div className={getClassName("kcInputWrapperClass")}>
<input
type="text"
id="lastName"
- className={clsx(kcProps.kcInputClass)}
+ className={getClassName("kcInputClass")}
name="lastName"
defaultValue={register.formData.lastName ?? ""}
/>
-
</div>
</div>
- <div className={clsx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("email", kcProps.kcFormGroupErrorClass))}>
- <div className={clsx(kcProps.kcLabelWrapperClass)}>
- <label htmlFor="email" className={clsx(kcProps.kcLabelClass)}>
+ <div
+ className={clsx(
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("email", getClassName("kcFormGroupErrorClass"))
+ )}
+ >
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="email" className={getClassName("kcLabelClass")}>
{msg("email")}
</label>
</div>
- <div className={clsx(kcProps.kcInputWrapperClass)}>
+ <div className={getClassName("kcInputWrapperClass")}>
<input
type="text"
id="email"
- className={clsx(kcProps.kcInputClass)}
+ className={getClassName("kcInputClass")}
name="email"
defaultValue={register.formData.email ?? ""}
autoComplete="email"
@@ -76,17 +91,22 @@ export default function Register(props: PageProps<Extract<KcContext, { pageId: "
</div>
</div>
{!realm.registrationEmailAsUsername && (
- <div className={clsx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("username", kcProps.kcFormGroupErrorClass))}>
- <div className={clsx(kcProps.kcLabelWrapperClass)}>
- <label htmlFor="username" className={clsx(kcProps.kcLabelClass)}>
+ <div
+ className={clsx(
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("username", getClassName("kcFormGroupErrorClass"))
+ )}
+ >
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="username" className={getClassName("kcLabelClass")}>
{msg("username")}
</label>
</div>
- <div className={clsx(kcProps.kcInputWrapperClass)}>
+ <div className={getClassName("kcInputWrapperClass")}>
<input
type="text"
id="username"
- className={clsx(kcProps.kcInputClass)}
+ className={getClassName("kcInputClass")}
name="username"
defaultValue={register.formData.username ?? ""}
autoComplete="username"
@@ -97,18 +117,21 @@ export default function Register(props: PageProps<Extract<KcContext, { pageId: "
{passwordRequired && (
<>
<div
- className={clsx(kcProps.kcFormGroupClass, messagesPerField.printIfExists("password", kcProps.kcFormGroupErrorClass))}
+ className={clsx(
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("password", getClassName("kcFormGroupErrorClass"))
+ )}
>
- <div className={clsx(kcProps.kcLabelWrapperClass)}>
- <label htmlFor="password" className={clsx(kcProps.kcLabelClass)}>
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="password" className={getClassName("kcLabelClass")}>
{msg("password")}
</label>
</div>
- <div className={clsx(kcProps.kcInputWrapperClass)}>
+ <div className={getClassName("kcInputWrapperClass")}>
<input
type="password"
id="password"
- className={clsx(kcProps.kcInputClass)}
+ className={getClassName("kcInputClass")}
name="password"
autoComplete="new-password"
/>
@@ -117,44 +140,44 @@ export default function Register(props: PageProps<Extract<KcContext, { pageId: "
<div
className={clsx(
- kcProps.kcFormGroupClass,
- messagesPerField.printIfExists("password-confirm", kcProps.kcFormGroupErrorClass)
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("password-confirm", getClassName("kcFormGroupErrorClass"))
)}
>
- <div className={clsx(kcProps.kcLabelWrapperClass)}>
- <label htmlFor="password-confirm" className={clsx(kcProps.kcLabelClass)}>
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="password-confirm" className={getClassName("kcLabelClass")}>
{msg("passwordConfirm")}
</label>
</div>
- <div className={clsx(kcProps.kcInputWrapperClass)}>
- <input type="password" id="password-confirm" className={clsx(kcProps.kcInputClass)} name="password-confirm" />
+ <div className={getClassName("kcInputWrapperClass")}>
+ <input type="password" id="password-confirm" className={getClassName("kcInputClass")} name="password-confirm" />
</div>
</div>
</>
)}
{recaptchaRequired && (
<div className="form-group">
- <div className={clsx(kcProps.kcInputWrapperClass)}>
+ <div className={getClassName("kcInputWrapperClass")}>
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
</div>
</div>
)}
- <div className={clsx(kcProps.kcFormGroupClass)}>
- <div id="kc-form-options" className={clsx(kcProps.kcFormOptionsClass)}>
- <div className={clsx(kcProps.kcFormOptionsWrapperClass)}>
+ <div className={getClassName("kcFormGroupClass")}>
+ <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
+ <div className={getClassName("kcFormOptionsWrapperClass")}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
- <div id="kc-form-buttons" className={clsx(kcProps.kcFormButtonsClass)}>
+ <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<input
className={clsx(
- kcProps.kcButtonClass,
- kcProps.kcButtonPrimaryClass,
- kcProps.kcButtonBlockClass,
- kcProps.kcButtonLargeClass
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonPrimaryClass"),
+ getClassName("kcButtonBlockClass"),
+ getClassName("kcButtonLargeClass")
)}
type="submit"
value={msgStr("doRegister")}
@@ -166,4 +189,3 @@ export default function Register(props: PageProps<Extract<KcContext, { pageId: "
/>
);
}
-
diff --git a/src/keycloak-theme/pages/RegisterUserProfile.tsx b/src/keycloak-theme/pages/RegisterUserProfile.tsx
index e3b1f77..48d791f 100644
--- a/src/keycloak-theme/pages/RegisterUserProfile.tsx
+++ b/src/keycloak-theme/pages/RegisterUserProfile.tsx
@@ -1,13 +1,20 @@
-// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/pages/RegisterUserProfile.tsx
+// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/pages/RegisterUserProfile.tsx
+
import { useState } from "react";
-import { clsx } from "keycloakify/lib/tools/clsx";
-import { UserProfileFormFields } from "keycloakify/lib/pages/shared/UserProfileCommons";
-import type { PageProps } from "keycloakify/lib/KcProps";
+import { clsx } from "keycloakify/tools/clsx";
+import { UserProfileFormFields } from "./shared/UserProfileCommons";
+import { type PageProps, defaultClasses } from "keycloakify/pages/PageProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
export default function RegisterUserProfile(props: PageProps<Extract<KcContext, { pageId: "register-user-profile.ftl" }>, I18n>) {
- const { kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps } = props;
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
+
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
+ classes
+ });
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
@@ -17,36 +24,41 @@ export default function RegisterUserProfile(props: PageProps<Extract<KcContext,
return (
<Template
- {...{ kcContext, i18n, doFetchDefaultThemeResources, ...kcProps }}
+ {...{ kcContext, i18n, doUseDefaultCss, classes }}
displayMessage={messagesPerField.exists("global")}
displayRequiredFields={true}
headerNode={msg("registerTitle")}
formNode={
- <form id="kc-register-form" className={clsx(kcProps.kcFormClass)} action={url.registrationAction} method="post">
- <UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...kcProps} />
+ <form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
+ <UserProfileFormFields
+ kcContext={kcContext}
+ onIsFormSubmittableValueChange={setIsFomSubmittable}
+ i18n={i18n}
+ getClassName={getClassName}
+ />
{recaptchaRequired && (
<div className="form-group">
- <div className={clsx(kcProps.kcInputWrapperClass)}>
+ <div className={getClassName("kcInputWrapperClass")}>
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
</div>
</div>
)}
- <div className={clsx(kcProps.kcFormGroupClass)} style={{ "marginBottom": 30 }}>
- <div id="kc-form-options" className={clsx(kcProps.kcFormOptionsClass)}>
- <div className={clsx(kcProps.kcFormOptionsWrapperClass)}>
+ <div className={getClassName("kcFormGroupClass")} style={{ "marginBottom": 30 }}>
+ <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
+ <div className={getClassName("kcFormOptionsWrapperClass")}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
- <div id="kc-form-buttons" className={clsx(kcProps.kcFormButtonsClass)}>
+ <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<input
className={clsx(
- kcProps.kcButtonClass,
- kcProps.kcButtonPrimaryClass,
- kcProps.kcButtonBlockClass,
- kcProps.kcButtonLargeClass
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonPrimaryClass"),
+ getClassName("kcButtonBlockClass"),
+ getClassName("kcButtonLargeClass")
)}
type="submit"
value={msgStr("doRegister")}
diff --git a/src/keycloak-theme/pages/Terms.tsx b/src/keycloak-theme/pages/Terms.tsx
index 69d1b99..19d490f 100644
--- a/src/keycloak-theme/pages/Terms.tsx
+++ b/src/keycloak-theme/pages/Terms.tsx
@@ -5,45 +5,28 @@
* in the KcApp.tsx
* Example: https://github.com/garronej/keycloakify-starter/blob/a20c21b2aae7c6dc6dbea294f3d321955ddf9355/src/KcApp/KcApp.tsx#L14-L30
*/
-import {clsx} from "keycloakify/lib/tools/clsx";
-import {useRerenderOnStateChange} from "evt/hooks";
-import {Markdown} from "keycloakify/lib/tools/Markdown";
-import {evtTermMarkdown, useDownloadTerms} from "keycloakify/lib/pages/Terms";
-import tos_en_url from "../assets/tos_en.md";
-import tos_fr_url from "../assets/tos_fr.md";
-import type {PageProps} from "keycloakify/lib/KcProps";
-import type {KcContext} from "../kcContext";
-import type {I18n} from "../i18n";
+import { clsx } from "keycloakify/tools/clsx";
+import { useRerenderOnStateChange } from "evt/hooks";
+import { Markdown } from "keycloakify/tools/Markdown";
+import { type PageProps, defaultClasses } from "keycloakify/pages/PageProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
+import { evtTermMarkdown } from "keycloakify/lib/useDownloadTerms";
+import type { KcContext } from "../kcContext";
+import type { I18n } from "../i18n";
-export default function Terms(props: PageProps<Extract<KcContext, { pageId: "terms.ftl"; }>, I18n>) {
- const {kcContext, i18n, doFetchDefaultThemeResources = true, Template, ...kcProps} = props;
+export default function Terms(props: PageProps<Extract<KcContext, { pageId: "terms.ftl" }>, I18n>) {
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
- const {msg, msgStr} = i18n;
-
- useDownloadTerms({
- kcContext,
- "downloadTermMarkdown": async ({currentLanguageTag}) => {
-
- const resource = (() => {
- switch (currentLanguageTag) {
- case "fr":
- return tos_fr_url;
- default:
- return tos_en_url;
- }
- })();
-
- // webpack5 (used via storybook) loads markdown as string, not url
- if (resource.includes("\n")) return resource
-
- const response = await fetch(resource);
- return response.text();
- },
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
+ classes
});
+ const { msg, msgStr } = i18n;
+
useRerenderOnStateChange(evtTermMarkdown);
- const {url} = kcContext;
+ const { url } = kcContext;
if (evtTermMarkdown.state === undefined) {
return null;
@@ -51,21 +34,20 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
return (
<Template
- {...{kcContext, i18n, doFetchDefaultThemeResources, ...kcProps}}
+ {...{ kcContext, i18n, doUseDefaultCss, classes }}
displayMessage={false}
headerNode={msg("termsTitle")}
formNode={
<>
- <div id="kc-terms-text">{evtTermMarkdown.state &&
- <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
+ <div id="kc-terms-text">{evtTermMarkdown.state && <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
<form className="form-actions" action={url.loginAction} method="POST">
<input
className={clsx(
- kcProps.kcButtonClass,
- kcProps.kcButtonClass,
- kcProps.kcButtonClass,
- kcProps.kcButtonPrimaryClass,
- kcProps.kcButtonLargeClass
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonPrimaryClass"),
+ getClassName("kcButtonLargeClass")
)}
name="accept"
id="kc-accept"
@@ -73,14 +55,14 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
value={msgStr("doAccept")}
/>
<input
- className={clsx(kcProps.kcButtonClass, kcProps.kcButtonDefaultClass, kcProps.kcButtonLargeClass)}
+ className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
name="cancel"
id="kc-decline"
type="submit"
value={msgStr("doDecline")}
/>
</form>
- <div className="clearfix"/>
+ <div className="clearfix" />
</>
}
/>