Small refactor

This commit is contained in:
garronej committed 2023-02-27 10:13:07 +01:00
1 parent 7b231748a2
commit 34868a4153
15 files changed
+12 -7

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
.my-color {
color: red;
}
.my-font {
font-family: 'Work Sans';
}
.my-root-class {
background: white;
}
.my-root-class body {
background: url(./assets/background.svg) no-repeat center center fixed;
}
+66
View File
@@ -0,0 +1,66 @@
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";
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 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"));
// This is like editing the theme.properties
// https://github.com/keycloak/keycloak/blob/11.0.3/themes/src/main/resources/theme/keycloak/login/theme.properties
const kcProps: KcProps = {
...defaultKcProps,
// 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
"kcHeaderWrapperClass": "my-color my-font"
};
export default function App(props: { kcContext: KcContext; }) {
const { kcContext } = props;
const i18n = useI18n({ kcContext });
//NOTE: Locales not yet downloaded
if (i18n === null) {
return null;
}
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 "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 }} />;
// 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 }} />;
}
})()}
</Suspense>
);
}
+235
View File
@@ -0,0 +1,235 @@
// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/components/shared/Template.tsx
import { useReducer, useEffect } from "react";
// You can replace all relative imports by cherry picking files from the keycloakify module.
// For example, the following import:
// import { headInsert } from "./tools/headInsert";
// becomes:
import { headInsert } from "keycloakify/lib/tools/headInsert";
import { assert } from "keycloakify/lib/tools/assert";
import { clsx } from "keycloakify/lib/tools/clsx";
import { pathJoin } from "keycloakify/bin/tools/pathJoin";
import type { TemplateProps } from "keycloakify/lib/KcProps";
import type { KcContext } from "./kcContext";
import type { I18n } from "./i18n";
export default function Template(props: TemplateProps<KcContext, I18n>) {
const {
displayInfo = false,
displayMessage = true,
displayRequiredFields = false,
displayWide = false,
showAnotherWayIfPresent = true,
headerNode,
showUsernameNode = null,
formNode,
infoNode = null,
kcContext,
i18n,
doFetchDefaultThemeResources
} = props;
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext;
const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false);
useEffect(() => {
if (!doFetchDefaultThemeResources) {
setExtraCssLoaded();
return;
}
let isUnmounted = false;
const cleanups: (() => void)[] = [];
const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []);
Promise.all(
[
...toArr(props.stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)),
...toArr(props.styles).map(relativePath => pathJoin(url.resourcesPath, relativePath))
]
.reverse()
.map(href =>
headInsert({
"type": "css",
href,
"position": "prepend"
})
)
).then(() => {
if (isUnmounted) {
return;
}
setExtraCssLoaded();
});
toArr(props.scripts).forEach(relativePath =>
headInsert({
"type": "javascript",
"src": pathJoin(url.resourcesPath, relativePath)
})
);
if (props.kcHtmlClass !== undefined) {
const htmlClassList = document.getElementsByTagName("html")[0].classList;
const tokens = clsx(props.kcHtmlClass).split(" ");
htmlClassList.add(...tokens);
cleanups.push(() => htmlClassList.remove(...tokens));
}
return () => {
isUnmounted = true;
cleanups.forEach(f => f());
};
}, [props.kcHtmlClass]);
if (!isExtraCssLoaded) {
return null;
}
return (
<div className={clsx(props.kcLoginClass)}>
<div id="kc-header" className={clsx(props.kcHeaderClass)}>
<div id="kc-header-wrapper" className={clsx(props.kcHeaderWrapperClass)}>
{msg("loginTitleHtml", realm.displayNameHtml)}
</div>
</div>
<div className={clsx(props.kcFormCardClass, displayWide && props.kcFormCardAccountClass)}>
<header className={clsx(props.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 className="kc-dropdown" id="kc-locale-dropdown">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" id="kc-current-locale-link">
{labelBySupportedLanguageTag[currentLanguageTag]}
</a>
<ul>
{locale.supported.map(({ languageTag }) => (
<li key={languageTag} className="kc-dropdown-item">
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#" onClick={() => changeLocale(languageTag)}>
{labelBySupportedLanguageTag[languageTag]}
</a>
</li>
))}
</ul>
</div>
</div>
</div>
)}
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
displayRequiredFields ? (
<div className={clsx(props.kcContentWrapperClass)}>
<div className={clsx(props.kcLabelWrapperClass, "subtitle")}>
<span className="subtitle">
<span className="required">*</span>
{msg("requiredFields")}
</span>
</div>
<div className="col-md-10">
<h1 id="kc-page-title">{headerNode}</h1>
</div>
</div>
) : (
<h1 id="kc-page-title">{headerNode}</h1>
)
) : displayRequiredFields ? (
<div className={clsx(props.kcContentWrapperClass)}>
<div className={clsx(props.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 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>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
</div>
</div>
</div>
</div>
) : (
<>
{showUsernameNode}
<div className={clsx(props.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>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
</div>
</div>
</>
)}
</header>
<div id="kc-content">
<div id="kc-content-wrapper">
{/* 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>}
<span
className="kc-feedback-text"
dangerouslySetInnerHTML={{
"__html": message.summary
}}
/>
</div>
)}
{formNode}
{auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && (
<form
id="kc-select-try-another-way-form"
action={url.loginAction}
method="post"
className={clsx(displayWide && props.kcContentWrapperClass)}
>
<div className={clsx(displayWide && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}>
<div className={clsx(props.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;
}}>
{msg("doTryAnotherWay")}
</a>
</div>
</div>
</form>
)}
{displayInfo && (
<div id="kc-info" className={clsx(props.kcSignUpClass)}>
<div id="kc-info-wrapper" className={clsx(props.kcInfoAreaWrapperClass)}>
{infoNode}
</div>
</div>
)}
</div>
</div>
</div>
</div>
);
}
Loaded 3 of 15 files, more files were not shown because too many files have changed in this diff. Show more