update setup
This commit is contained in:
1 parent
39ae948f24
commit
93780a9702
10 files changed
+422
-411
No files matched your search
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
+21
-29
@@ -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>>;
|
||||
Loaded 3 of 10 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user