getClassName -> kcClsx

This commit is contained in:
Joseph Garrone
2024-06-09 08:27:07 +02:00
parent 8f006f0009
commit 1a48681591
61 changed files with 878 additions and 1056 deletions

View File

@ -2,7 +2,6 @@ import { lazy, Suspense } from "react";
import { assert, type Equals } from "tsafe/assert";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "./KcContext";
import type { I18n } from "./i18n";
const Password = lazy(() => import("keycloakify/account/pages/Password"));
const Account = lazy(() => import("keycloakify/account/pages/Account"));
@ -12,7 +11,7 @@ const Applications = lazy(() => import("keycloakify/account/pages/Applications")
const Log = lazy(() => import("keycloakify/account/pages/Log"));
const FederatedIdentity = lazy(() => import("keycloakify/account/pages/FederatedIdentity"));
export default function Fallback(props: PageProps<KcContext, I18n>) {
export default function Fallback(props: PageProps<KcContext>) {
const { kcContext, ...rest } = props;
return (

View File

@ -1,7 +1,7 @@
import { useEffect } from "react";
import { assert } from "keycloakify/tools/assert";
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import { useInsertLinkTags } from "keycloakify/tools/useInsertLinkTags";
import { useSetClassName } from "keycloakify/tools/useSetClassName";
import type { TemplateProps } from "keycloakify/account/TemplateProps";
@ -11,7 +11,7 @@ import { useI18n } from "./i18n";
export default function Template(props: TemplateProps<KcContext>) {
const { kcContext, doUseDefaultCss, active, classes, children } = props;
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
const { msg, msgStr, getChangeLocalUrl, labelBySupportedLanguageTag, currentLanguageTag } = useI18n({ kcContext });
@ -23,12 +23,12 @@ export default function Template(props: TemplateProps<KcContext>) {
useSetClassName({
qualifiedName: "html",
className: getClassName("kcHtmlClass")
className: kcClsx("kcHtmlClass")
});
useSetClassName({
qualifiedName: "body",
className: clsx("admin-console", "user", getClassName("kcBodyClass"))
className: clsx("admin-console", "user", kcClsx("kcBodyClass"))
});
useEffect(() => {

View File

@ -1,7 +1,7 @@
import { createUseClassName } from "keycloakify/lib/useGetClassName";
import { createGetKcClsx } from "keycloakify/lib/getKcClsx";
import type { ClassKey } from "keycloakify/account/TemplateProps";
export const { useGetClassName } = createUseClassName<ClassKey>({
export const { getKcClsx } = createGetKcClsx<ClassKey>({
defaultClasses: {
kcHtmlClass: undefined,
kcBodyClass: undefined,
@ -19,3 +19,7 @@ export const { useGetClassName } = createUseClassName<ClassKey>({
"pf-c-form__helper-text pf-m-error required kc-feedback-text"
}
});
export type { ClassKey };
export type KcClsx = ReturnType<typeof getKcClsx>["kcClsx"];

View File

@ -1,18 +1,20 @@
import { clsx } from "keycloakify/tools/clsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
export default function Account(props: PageProps<Extract<KcContext, { pageId: "account.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { kcContext, doUseDefaultCss, Template } = props;
const { getClassName } = useGetClassName({
const classes = {
...props.classes,
kcBodyClass: clsx(props.classes?.kcBodyClass, "user")
};
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes: {
...classes,
kcBodyClass: clsx(classes?.kcBodyClass, "user")
}
classes
});
const { url, realm, messagesPerField, stateChecker, account, referrer } = kcContext;
@ -102,11 +104,7 @@ export default function Account(props: PageProps<Extract<KcContext, { pageId: "a
{referrer !== undefined && <a href={referrer?.url}>{msg("backToApplication")}</a>}
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
name="submitAction"
value="Save"
>
@ -114,11 +112,7 @@ export default function Account(props: PageProps<Extract<KcContext, { pageId: "a
</button>
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
name="submitAction"
value="Cancel"
>

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n } from "../i18n";
export default function Applications(props: PageProps<Extract<KcContext, { pageId: "applications.ftl" }>>) {
const { kcContext, doUseDefaultCss, classes, Template } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -114,7 +113,7 @@ export default function Applications(props: PageProps<Extract<KcContext, { pageI
application.additionalGrants.length > 0 ? (
<button
type="submit"
className={clsx(getClassName("kcButtonPrimaryClass"), getClassName("kcButtonClass"))}
className={kcClsx("kcButtonPrimaryClass", "kcButtonClass")}
id={`revoke-${application.client.clientId}`}
name="clientId"
value={application.client.id}

View File

@ -1,5 +1,5 @@
import type { Key } from "react";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +7,7 @@ import { useI18n } from "../i18n";
export default function Log(props: PageProps<Extract<KcContext, { pageId: "log.ftl" }>>) {
const { kcContext, doUseDefaultCss, classes, Template } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -18,7 +18,7 @@ export default function Log(props: PageProps<Extract<KcContext, { pageId: "log.f
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} active="log">
<div className={getClassName("kcContentWrapperClass")}>
<div className={kcClsx("kcContentWrapperClass")}>
<div className="col-md-10">
<h2>{msg("accountLogHtmlTitle")}</h2>
</div>

View File

@ -1,19 +1,21 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
export default function Password(props: PageProps<Extract<KcContext, { pageId: "password.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { kcContext, doUseDefaultCss, Template } = props;
const { getClassName } = useGetClassName({
const classes = {
...props.classes,
kcBodyClass: clsx(props.classes?.kcBodyClass, "password")
};
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes: {
...classes,
kcBodyClass: clsx(classes?.kcBodyClass, "password")
}
classes
});
const { url, password, account, stateChecker } = kcContext;
@ -191,11 +193,7 @@ export default function Password(props: PageProps<Extract<KcContext, { pageId: "
<button
disabled={newPasswordError !== "" || newPasswordConfirmError !== ""}
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
name="submitAction"
value="Save"
>

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n } from "../i18n";
export default function Sessions(props: PageProps<Extract<KcContext, { pageId: "sessions.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -17,7 +16,7 @@ export default function Sessions(props: PageProps<Extract<KcContext, { pageId: "
const { msg } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} active="sessions">
<div className={getClassName("kcContentWrapperClass")}>
<div className={kcClsx("kcContentWrapperClass")}>
<div className="col-md-10">
<h2>{msg("sessionsHtmlTitle")}</h2>
</div>
@ -56,7 +55,7 @@ export default function Sessions(props: PageProps<Extract<KcContext, { pageId: "
<form action={url.sessionsUrl} method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
<button id="logout-all-sessions" type="submit" className={clsx(getClassName("kcButtonDefaultClass"), getClassName("kcButtonClass"))}>
<button id="logout-all-sessions" type="submit" className={kcClsx("kcButtonDefaultClass", "kcButtonClass")}>
{msg("doLogOutAllSessions")}
</button>
</form>

View File

@ -1,5 +1,5 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +7,7 @@ import { useI18n } from "../i18n";
export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -140,9 +140,9 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
</li>
</ol>
<hr />
<form action={url.totpUrl} className={getClassName("kcFormClass")} id="kc-totp-settings-form" method="post">
<form action={url.totpUrl} className={kcClsx("kcFormClass")} id="kc-totp-settings-form" method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
<div className={getClassName("kcFormGroupClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div className="col-sm-2 col-md-2">
<label htmlFor="totp" className="control-label">
{msg("authenticatorCode")}
@ -155,12 +155,12 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
id="totp"
name="totp"
autoComplete="off"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
aria-invalid={messagesPerField.existsError("totp")}
/>
{messagesPerField.existsError("totp") && (
<span id="input-error-otp-code" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-otp-code" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("totp")}
</span>
)}
@ -169,9 +169,9 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
{mode && <input type="hidden" id="mode" value={mode} />}
</div>
<div className={getClassName("kcFormGroupClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div className="col-sm-2 col-md-2">
<label htmlFor="userLabel" className={getClassName("kcLabelClass")}>
<label htmlFor="userLabel" className={kcClsx("kcLabelClass")}>
{msg("totpDeviceName")}
</label>
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
@ -182,37 +182,28 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
id="userLabel"
name="userLabel"
autoComplete="off"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
aria-invalid={messagesPerField.existsError("userLabel")}
/>
{messagesPerField.existsError("userLabel") && (
<span id="input-error-otp-label" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-otp-label" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("userLabel")}
</span>
)}
</div>
</div>
<div id="kc-form-buttons" className={clsx(getClassName("kcFormGroupClass"), "text-right")}>
<div className={getClassName("kcInputWrapperClass")}>
<div id="kc-form-buttons" className={clsx(kcClsx("kcFormGroupClass"), "text-right")}>
<div className={kcClsx("kcInputWrapperClass")}>
<input
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
id="saveTOTPBtn"
value={msgStr("doSave")}
/>
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass", "kcButtonLargeClass")}
id="cancelTOTPBtn"
name="submitAction"
value="Cancel"

75
src/lib/getKcClsx.ts Normal file
View File

@ -0,0 +1,75 @@
import type { Param0 } from "tsafe";
import { type CxArg, clsx_withTransform } from "../tools/clsx_withTransform";
import { clsx } from "../tools/clsx";
import { assert } from "tsafe/assert";
import { is } from "tsafe/is";
export function createGetKcClsx<ClassKey extends string>(params: {
defaultClasses: Record<ClassKey, string | undefined>;
}) {
const { defaultClasses } = params;
function areSameParams(
params1: Param0<typeof getKcClsx>,
params2: Param0<typeof getKcClsx>
): boolean {
if (params1.doUseDefaultCss !== params2.doUseDefaultCss) {
return false;
}
if (params1.classes === params2.classes) {
return true;
}
return JSON.stringify(params1.classes) === JSON.stringify(params2.classes);
}
let cache:
| {
params: Param0<typeof getKcClsx>;
result: ReturnType<typeof getKcClsx>;
}
| undefined = undefined;
function getKcClsx(params: {
doUseDefaultCss: boolean;
classes: Partial<Record<ClassKey, string>> | undefined;
}): { kcClsx: (...args: CxArg<ClassKey>[]) => string } {
// NOTE: We implement a cache here only so that getClassName can be stable across renders.
// We don't want to use useConstCallback because we want this to be useable outside of React.
use_cache: {
if (cache === undefined) {
break use_cache;
}
if (!areSameParams(cache.params, params)) {
break use_cache;
}
return cache.result;
}
const { classes, doUseDefaultCss } = params;
function kcClsx(...args: CxArg<ClassKey>[]): string {
return clsx_withTransform({
args,
transform: classKey => {
assert(is<ClassKey>(classKey));
return clsx(
classKey,
doUseDefaultCss ? defaultClasses[classKey] : undefined,
classes?.[classKey]
);
}
});
}
cache = { params, result: { kcClsx } };
return { kcClsx };
}
return { getKcClsx };
}

View File

@ -1,66 +0,0 @@
import { clsx } from "keycloakify/tools/clsx";
import type { Param0 } from "tsafe";
// NOTE: Note for people trying to implement Keycloakify in other frontend
// frameworks. This can be used outside of React, useGetClassName isn't actually a hook.
export function createUseClassName<ClassKey extends string>(params: {
defaultClasses: Record<ClassKey, string | undefined>;
}) {
const { defaultClasses } = params;
function areSameParams(
params1: Param0<typeof useGetClassName>,
params2: Param0<typeof useGetClassName>
): boolean {
if (params1.doUseDefaultCss !== params2.doUseDefaultCss) {
return false;
}
if (params1.classes === params2.classes) {
return true;
}
return JSON.stringify(params1.classes) === JSON.stringify(params2.classes);
}
let cache:
| {
params: Param0<typeof useGetClassName>;
result: ReturnType<typeof useGetClassName>;
}
| undefined = undefined;
function useGetClassName(params: {
doUseDefaultCss: boolean;
classes: Partial<Record<ClassKey, string>> | undefined;
}): { getClassName: (classKey: ClassKey) => string } {
// NOTE: We implement a cache here only so that getClassName can be stable across renders.
// We don't want to use useConstCallback because we want this to be useable outside of React.
use_cache: {
if (cache === undefined) {
break use_cache;
}
if (!areSameParams(cache.params, params)) {
break use_cache;
}
return cache.result;
}
function getClassName(classKey: ClassKey): string {
return clsx(
classKey,
params.doUseDefaultCss ? defaultClasses[classKey] : undefined,
params.classes?.[classKey]
);
}
cache = { params, result: { getClassName } };
return { getClassName };
}
return { useGetClassName };
}

View File

@ -2,7 +2,7 @@ import { useEffect } from "react";
import { assert } from "keycloakify/tools/assert";
import { clsx } from "keycloakify/tools/clsx";
import type { TemplateProps } from "keycloakify/login/TemplateProps";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
import { useInsertLinkTags } from "keycloakify/tools/useInsertLinkTags";
import { useSetClassName } from "keycloakify/tools/useSetClassName";
@ -26,7 +26,7 @@ export default function Template(props: TemplateProps<KcContext>) {
children
} = props;
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
const { msg, msgStr, getChangeLocalUrl, labelBySupportedLanguageTag, currentLanguageTag } = useI18n({ kcContext });
@ -38,12 +38,12 @@ export default function Template(props: TemplateProps<KcContext>) {
useSetClassName({
qualifiedName: "html",
className: getClassName("kcHtmlClass")
className: kcClsx("kcHtmlClass")
});
useSetClassName({
qualifiedName: "body",
className: bodyClassName ?? getClassName("kcBodyClass")
className: bodyClassName ?? kcClsx("kcBodyClass")
});
useEffect(() => {
@ -115,19 +115,19 @@ export default function Template(props: TemplateProps<KcContext>) {
}
return (
<div className={getClassName("kcLoginClass")}>
<div id="kc-header" className={getClassName("kcHeaderClass")}>
<div id="kc-header-wrapper" className={getClassName("kcHeaderWrapperClass")}>
<div className={kcClsx("kcLoginClass")}>
<div id="kc-header" className={kcClsx("kcHeaderClass")}>
<div id="kc-header-wrapper" className={kcClsx("kcHeaderWrapperClass")}>
{msg("loginTitleHtml", realm.displayNameHtml)}
</div>
</div>
<div className={getClassName("kcFormCardClass")}>
<header className={getClassName("kcFormHeaderClass")}>
<div className={kcClsx("kcFormCardClass")}>
<header className={kcClsx("kcFormHeaderClass")}>
{realm.internationalizationEnabled && (assert(locale !== undefined), locale.supported.length > 1) && (
<div className={getClassName("kcLocaleMainClass")} id="kc-locale">
<div id="kc-locale-wrapper" className={getClassName("kcLocaleWrapperClass")}>
<div id="kc-locale-dropdown" className={clsx("menu-button-links", getClassName("kcLocaleDropDownClass"))}>
<div className={kcClsx("kcLocaleMainClass")} id="kc-locale">
<div id="kc-locale-wrapper" className={kcClsx("kcLocaleWrapperClass")}>
<div id="kc-locale-dropdown" className={clsx("menu-button-links", kcClsx("kcLocaleDropDownClass"))}>
<button
tabIndex={1}
id="kc-current-locale-link"
@ -144,14 +144,14 @@ export default function Template(props: TemplateProps<KcContext>) {
aria-labelledby="kc-current-locale-link"
aria-activedescendant=""
id="language-switch1"
className={getClassName("kcLocaleListClass")}
className={kcClsx("kcLocaleListClass")}
>
{locale.supported.map(({ languageTag }, i) => (
<li key={languageTag} className={getClassName("kcLocaleListItemClass")} role="none">
<li key={languageTag} className={kcClsx("kcLocaleListItemClass")} role="none">
<a
role="menuitem"
id={`language-${i + 1}`}
className={getClassName("kcLocaleItemClass")}
className={kcClsx("kcLocaleItemClass")}
href={getChangeLocalUrl(languageTag)}
>
{labelBySupportedLanguageTag[languageTag]}
@ -165,8 +165,8 @@ export default function Template(props: TemplateProps<KcContext>) {
)}
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
displayRequiredFields ? (
<div className={getClassName("kcContentWrapperClass")}>
<div className={clsx(getClassName("kcLabelWrapperClass"), "subtitle")}>
<div className={kcClsx("kcContentWrapperClass")}>
<div className={clsx(kcClsx("kcLabelWrapperClass"), "subtitle")}>
<span className="subtitle">
<span className="required">*</span>
{msg("requiredFields")}
@ -180,19 +180,19 @@ export default function Template(props: TemplateProps<KcContext>) {
<h1 id="kc-page-title">{headerNode}</h1>
)
) : displayRequiredFields ? (
<div className={getClassName("kcContentWrapperClass")}>
<div className={clsx(getClassName("kcLabelWrapperClass"), "subtitle")}>
<div className={kcClsx("kcContentWrapperClass")}>
<div className={clsx(kcClsx("kcLabelWrapperClass"), "subtitle")}>
<span className="subtitle">
<span className="required">*</span> {msg("requiredFields")}
</span>
</div>
<div className="col-md-10">
{showUsernameNode}
<div id="kc-username" className={getClassName("kcFormGroupClass")}>
<div id="kc-username" className={kcClsx("kcFormGroupClass")}>
<label id="kc-attempted-username">{auth.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl} aria-label={msgStr("restartLoginTooltip")}>
<div className="kc-login-tooltip">
<i className={getClassName("kcResetFlowIcon")}></i>
<i className={kcClsx("kcResetFlowIcon")}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
@ -202,11 +202,11 @@ export default function Template(props: TemplateProps<KcContext>) {
) : (
<>
{showUsernameNode}
<div id="kc-username" className={getClassName("kcFormGroupClass")}>
<div id="kc-username" className={kcClsx("kcFormGroupClass")}>
<label id="kc-attempted-username">{auth.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl} aria-label={msgStr("restartLoginTooltip")}>
<div className="kc-login-tooltip">
<i className={getClassName("kcResetFlowIcon")}></i>
<i className={kcClsx("kcResetFlowIcon")}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
@ -221,18 +221,18 @@ export default function Template(props: TemplateProps<KcContext>) {
<div
className={clsx(
`alert-${message.type}`,
getClassName("kcAlertClass"),
kcClsx("kcAlertClass"),
`pf-m-${message?.type === "error" ? "danger" : message.type}`
)}
>
<div className="pf-c-alert__icon">
{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>}
{message.type === "success" && <span className={kcClsx("kcFeedbackSuccessIcon")}></span>}
{message.type === "warning" && <span className={kcClsx("kcFeedbackWarningIcon")}></span>}
{message.type === "error" && <span className={kcClsx("kcFeedbackErrorIcon")}></span>}
{message.type === "info" && <span className={kcClsx("kcFeedbackInfoIcon")}></span>}
</div>
<span
className={getClassName("kcAlertTitleClass")}
className={kcClsx("kcAlertTitleClass")}
dangerouslySetInnerHTML={{
__html: message.summary
}}
@ -242,8 +242,8 @@ export default function Template(props: TemplateProps<KcContext>) {
{children}
{auth !== undefined && auth.showTryAnotherWayLink && (
<form id="kc-select-try-another-way-form" action={url.loginAction} method="post">
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcFormGroupClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<input type="hidden" name="tryAnotherWay" value="on" />
<a
href="#"
@ -261,8 +261,8 @@ export default function Template(props: TemplateProps<KcContext>) {
)}
{socialProvidersNode}
{displayInfo && (
<div id="kc-info" className={getClassName("kcSignUpClass")}>
<div id="kc-info-wrapper" className={getClassName("kcInfoAreaWrapperClass")}>
<div id="kc-info" className={kcClsx("kcSignUpClass")}>
<div id="kc-info-wrapper" className={kcClsx("kcInfoAreaWrapperClass")}>
{infoNode}
</div>
</div>

View File

@ -1,6 +1,6 @@
import { useEffect, useReducer, Fragment } from "react";
import { assert } from "tsafe/assert";
import type { ClassKey } from "keycloakify/login/TemplateProps";
import type { KcClsx } from "keycloakify/login/lib/kcClsx";
import {
useUserProfileForm,
getButtonToDisplayForMultivaluedAttributeField,
@ -9,11 +9,11 @@ import {
type FormFieldError
} from "keycloakify/login/lib/useUserProfileForm";
import type { Attribute } from "keycloakify/login/KcContext";
import { useI18n } from "./i18n";
import { useI18n, type I18n } from "./i18n";
export type UserProfileFormFieldsProps = {
kcContext: KcContextLike;
getClassName: (classKey: ClassKey) => string;
kcClsx: KcClsx;
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
BeforeField?: (props: BeforeAfterFieldProps) => JSX.Element | null;
AfterField?: (props: BeforeAfterFieldProps) => JSX.Element | null;
@ -23,24 +23,24 @@ type BeforeAfterFieldProps = {
attribute: Attribute;
dispatchFormAction: React.Dispatch<FormAction>;
displayableErrors: FormFieldError[];
i18n: I18n;
valueOrValues: string | string[];
kcClsx: KcClsx;
i18n: I18n;
};
// NOTE: Enabled by default but it's a UX best practice to set it to false.
const doMakeUserConfirmPassword = true;
export default function UserProfileFormFields(props: UserProfileFormFieldsProps) {
const { kcContext, onIsFormSubmittableValueChange, i18n, getClassName, BeforeField, AfterField } = props;
const { kcContext, kcClsx, onIsFormSubmittableValueChange, BeforeField, AfterField } = props;
const { advancedMsg } = i18n;
const { advancedMsg } = useI18n({ kcContext });
const {
formState: { formFieldStates, isFormSubmittable },
dispatchFormAction
} = useUserProfileForm({
kcContext,
i18n,
doMakeUserConfirmPassword
});
@ -48,6 +48,8 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps)
onIsFormSubmittableValueChange(isFormSubmittable);
}, [isFormSubmittable]);
const i18n = useI18n({ kcContext });
const groupNameRef = { current: "" };
return (
@ -55,32 +57,33 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps)
{formFieldStates.map(({ attribute, displayableErrors, valueOrValues }) => {
return (
<Fragment key={attribute.name}>
<GroupLabel attribute={attribute} getClassName={getClassName} i18n={i18n} groupNameRef={groupNameRef} />
<GroupLabel attribute={attribute} groupNameRef={groupNameRef} i18n={i18n} kcClsx={kcClsx} />
{BeforeField !== undefined && (
<BeforeField
attribute={attribute}
dispatchFormAction={dispatchFormAction}
displayableErrors={displayableErrors}
i18n={i18n}
valueOrValues={valueOrValues}
kcClsx={kcClsx}
i18n={i18n}
/>
)}
<div
className={getClassName("kcFormGroupClass")}
className={kcClsx("kcFormGroupClass")}
style={{
display: attribute.name === "password-confirm" && !doMakeUserConfirmPassword ? "none" : undefined
}}
>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor={attribute.name} className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor={attribute.name} className={kcClsx("kcLabelClass")}>
{advancedMsg(attribute.displayName ?? "")}
</label>
{attribute.required && <>*</>}
</div>
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
{attribute.annotations.inputHelperTextBefore !== undefined && (
<div
className={getClassName("kcInputHelperTextBeforeClass")}
className={kcClsx("kcInputHelperTextBeforeClass")}
id={`form-help-text-before-${attribute.name}`}
aria-live="polite"
>
@ -91,19 +94,14 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps)
attribute={attribute}
valueOrValues={valueOrValues}
displayableErrors={displayableErrors}
formValidationDispatch={dispatchFormAction}
getClassName={getClassName}
dispatchFormAction={dispatchFormAction}
kcClsx={kcClsx}
i18n={i18n}
/>
<FieldErrors
attribute={attribute}
getClassName={getClassName}
displayableErrors={displayableErrors}
fieldIndex={undefined}
/>
<FieldErrors attribute={attribute} displayableErrors={displayableErrors} kcClsx={kcClsx} fieldIndex={undefined} />
{attribute.annotations.inputHelperTextAfter !== undefined && (
<div
className={getClassName("kcInputHelperTextAfterClass")}
className={kcClsx("kcInputHelperTextAfterClass")}
id={`form-help-text-after-${attribute.name}`}
aria-live="polite"
>
@ -116,8 +114,9 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps)
attribute={attribute}
dispatchFormAction={dispatchFormAction}
displayableErrors={displayableErrors}
i18n={i18n}
valueOrValues={valueOrValues}
kcClsx={kcClsx}
i18n={i18n}
/>
)}
{/* NOTE: Downloading of html5DataAnnotations scripts is done in the useUserProfileForm hook */}
@ -132,13 +131,13 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps)
function GroupLabel(props: {
attribute: Attribute;
getClassName: UserProfileFormFieldsProps["getClassName"];
i18n: I18n;
groupNameRef: {
current: string;
};
i18n: I18n;
kcClsx: KcClsx;
}) {
const { attribute, getClassName, i18n, groupNameRef } = props;
const { attribute, groupNameRef, i18n, kcClsx } = props;
const { advancedMsg } = i18n;
@ -150,7 +149,7 @@ function GroupLabel(props: {
return (
<div
className={getClassName("kcFormGroupClass")}
className={kcClsx("kcFormGroupClass")}
{...Object.fromEntries(Object.entries(attribute.group.html5DataAnnotations).map(([key, value]) => [`data-${key}`, value]))}
>
{(() => {
@ -158,8 +157,8 @@ function GroupLabel(props: {
const groupHeaderText = groupDisplayHeader !== "" ? advancedMsg(groupDisplayHeader) : attribute.group.name;
return (
<div className={getClassName("kcContentWrapperClass")}>
<label id={`header-${attribute.group.name}`} className={getClassName("kcFormGroupHeader")}>
<div className={kcClsx("kcContentWrapperClass")}>
<label id={`header-${attribute.group.name}`} className={kcClsx("kcFormGroupHeader")}>
{groupHeaderText}
</label>
</div>
@ -172,8 +171,8 @@ function GroupLabel(props: {
const groupDescriptionText = advancedMsg(groupDisplayDescription);
return (
<div className={getClassName("kcLabelWrapperClass")}>
<label id={`description-${attribute.group.name}`} className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label id={`description-${attribute.group.name}`} className={kcClsx("kcLabelClass")}>
{groupDescriptionText}
</label>
</div>
@ -190,13 +189,8 @@ function GroupLabel(props: {
return null;
}
function FieldErrors(props: {
attribute: Attribute;
getClassName: UserProfileFormFieldsProps["getClassName"];
displayableErrors: FormFieldError[];
fieldIndex: number | undefined;
}) {
const { attribute, getClassName, fieldIndex } = props;
function FieldErrors(props: { attribute: Attribute; displayableErrors: FormFieldError[]; fieldIndex: number | undefined; kcClsx: KcClsx }) {
const { attribute, fieldIndex, kcClsx } = props;
const displayableErrors = props.displayableErrors.filter(error => error.fieldIndex === fieldIndex);
@ -207,7 +201,7 @@ function FieldErrors(props: {
return (
<span
id={`input-error-${attribute.name}${fieldIndex === undefined ? "" : `-${fieldIndex}`}`}
className={getClassName("kcInputErrorMessageClass")}
className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
>
{displayableErrors
@ -226,9 +220,9 @@ type InputFiledByTypeProps = {
attribute: Attribute;
valueOrValues: string | string[];
displayableErrors: FormFieldError[];
formValidationDispatch: React.Dispatch<FormAction>;
getClassName: UserProfileFormFieldsProps["getClassName"];
dispatchFormAction: React.Dispatch<FormAction>;
i18n: I18n;
kcClsx: KcClsx;
};
function InputFiledByType(props: InputFiledByTypeProps) {
@ -258,7 +252,7 @@ function InputFiledByType(props: InputFiledByTypeProps) {
if (attribute.name === "password" || attribute.name === "password-confirm") {
return (
<PasswordWrapper getClassName={props.getClassName} i18n={props.i18n} passwordInputId={attribute.name}>
<PasswordWrapper kcClsx={props.kcClsx} i18n={props.i18n} passwordInputId={attribute.name}>
{inputNode}
</PasswordWrapper>
);
@ -269,8 +263,8 @@ function InputFiledByType(props: InputFiledByTypeProps) {
}
}
function PasswordWrapper(props: { getClassName: (classKey: ClassKey) => string; i18n: I18n; passwordInputId: string; children: JSX.Element }) {
const { getClassName, i18n, passwordInputId, children } = props;
function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: string; children: JSX.Element }) {
const { kcClsx, i18n, passwordInputId, children } = props;
const { msgStr } = i18n;
@ -285,26 +279,23 @@ function PasswordWrapper(props: { getClassName: (classKey: ClassKey) => string;
}, [isPasswordRevealed]);
return (
<div className={getClassName("kcInputGroup")}>
<div className={kcClsx("kcInputGroup")}>
{children}
<button
type="button"
className={getClassName("kcFormPasswordVisibilityButtonClass")}
className={kcClsx("kcFormPasswordVisibilityButtonClass")}
aria-label={msgStr(isPasswordRevealed ? "hidePassword" : "showPassword")}
aria-controls={passwordInputId}
onClick={toggleIsPasswordRevealed}
>
<i
className={getClassName(isPasswordRevealed ? "kcFormPasswordVisibilityIconHide" : "kcFormPasswordVisibilityIconShow")}
aria-hidden
/>
<i className={kcClsx(isPasswordRevealed ? "kcFormPasswordVisibilityIconHide" : "kcFormPasswordVisibilityIconShow")} aria-hidden />
</button>
</div>
);
}
function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefined }) {
const { attribute, fieldIndex, getClassName, formValidationDispatch, valueOrValues, i18n, displayableErrors } = props;
const { attribute, fieldIndex, kcClsx, dispatchFormAction, valueOrValues, i18n, displayableErrors } = props;
return (
<>
@ -330,7 +321,7 @@ function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefine
return valueOrValues;
})()}
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
aria-invalid={displayableErrors.find(error => error.fieldIndex === fieldIndex) !== undefined}
disabled={attribute.readOnly}
autoComplete={attribute.autocomplete}
@ -348,7 +339,7 @@ function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefine
step={attribute.annotations.inputTypeStep}
{...Object.fromEntries(Object.entries(attribute.html5DataAnnotations ?? {}).map(([key, value]) => [`data-${key}`, value]))}
onChange={event =>
formValidationDispatch({
dispatchFormAction({
action: "update",
name: attribute.name,
valueOrValues: (() => {
@ -369,7 +360,7 @@ function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefine
})
}
onBlur={() =>
formValidationDispatch({
dispatchFormAction({
action: "focus lost",
name: attribute.name,
fieldIndex: fieldIndex
@ -387,17 +378,12 @@ function InputTag(props: InputFiledByTypeProps & { fieldIndex: number | undefine
return (
<>
<FieldErrors
attribute={attribute}
getClassName={getClassName}
displayableErrors={displayableErrors}
fieldIndex={fieldIndex}
/>
<FieldErrors attribute={attribute} kcClsx={kcClsx} displayableErrors={displayableErrors} fieldIndex={fieldIndex} />
<AddRemoveButtonsMultiValuedAttribute
attribute={attribute}
values={values}
fieldIndex={fieldIndex}
dispatchFormAction={formValidationDispatch}
dispatchFormAction={dispatchFormAction}
i18n={i18n}
/>
</>
@ -464,7 +450,7 @@ function AddRemoveButtonsMultiValuedAttribute(props: {
}
function InputTagSelects(props: InputFiledByTypeProps) {
const { attribute, formValidationDispatch, getClassName, valueOrValues } = props;
const { attribute, dispatchFormAction, kcClsx, valueOrValues } = props;
const { advancedMsg } = props.i18n;
@ -477,16 +463,16 @@ function InputTagSelects(props: InputFiledByTypeProps) {
case "select-radiobuttons":
return {
inputType: "radio",
classDiv: getClassName("kcInputClassRadio"),
classInput: getClassName("kcInputClassRadioInput"),
classLabel: getClassName("kcInputClassRadioLabel")
classDiv: kcClsx("kcInputClassRadio"),
classInput: kcClsx("kcInputClassRadioInput"),
classLabel: kcClsx("kcInputClassRadioLabel")
};
case "multiselect-checkboxes":
return {
inputType: "checkbox",
classDiv: getClassName("kcInputClassCheckbox"),
classInput: getClassName("kcInputClassCheckboxInput"),
classLabel: getClassName("kcInputClassCheckboxLabel")
classDiv: kcClsx("kcInputClassCheckbox"),
classInput: kcClsx("kcInputClassCheckboxInput"),
classLabel: kcClsx("kcInputClassCheckboxLabel")
};
}
})();
@ -529,7 +515,7 @@ function InputTagSelects(props: InputFiledByTypeProps) {
disabled={attribute.readOnly}
checked={valueOrValues instanceof Array ? valueOrValues.includes(option) : valueOrValues === option}
onChange={event =>
formValidationDispatch({
dispatchFormAction({
action: "update",
name: attribute.name,
valueOrValues: (() => {
@ -552,7 +538,7 @@ function InputTagSelects(props: InputFiledByTypeProps) {
})
}
onBlur={() =>
formValidationDispatch({
dispatchFormAction({
action: "focus lost",
name: attribute.name,
fieldIndex: undefined
@ -561,7 +547,7 @@ function InputTagSelects(props: InputFiledByTypeProps) {
/>
<label
htmlFor={`${attribute.name}-${option}`}
className={`${classLabel}${attribute.readOnly ? ` ${getClassName("kcInputClassRadioCheckboxLabelDisabled")}` : ""}`}
className={`${classLabel}${attribute.readOnly ? ` ${kcClsx("kcInputClassRadioCheckboxLabelDisabled")}` : ""}`}
>
{advancedMsg(option)}
</label>
@ -572,7 +558,7 @@ function InputTagSelects(props: InputFiledByTypeProps) {
}
function TextareaTag(props: InputFiledByTypeProps) {
const { attribute, formValidationDispatch, getClassName, displayableErrors, valueOrValues } = props;
const { attribute, dispatchFormAction, kcClsx, displayableErrors, valueOrValues } = props;
assert(typeof valueOrValues === "string");
@ -582,7 +568,7 @@ function TextareaTag(props: InputFiledByTypeProps) {
<textarea
id={attribute.name}
name={attribute.name}
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
aria-invalid={displayableErrors.length !== 0}
disabled={attribute.readOnly}
cols={attribute.annotations.inputTypeCols === undefined ? undefined : parseInt(`${attribute.annotations.inputTypeCols}`)}
@ -590,14 +576,14 @@ function TextareaTag(props: InputFiledByTypeProps) {
maxLength={attribute.annotations.inputTypeMaxlength === undefined ? undefined : parseInt(`${attribute.annotations.inputTypeMaxlength}`)}
value={value}
onChange={event =>
formValidationDispatch({
dispatchFormAction({
action: "update",
name: attribute.name,
valueOrValues: event.target.value
})
}
onBlur={() =>
formValidationDispatch({
dispatchFormAction({
action: "focus lost",
name: attribute.name,
fieldIndex: undefined
@ -608,7 +594,7 @@ function TextareaTag(props: InputFiledByTypeProps) {
}
function SelectTag(props: InputFiledByTypeProps) {
const { attribute, formValidationDispatch, getClassName, displayableErrors, i18n, valueOrValues } = props;
const { attribute, dispatchFormAction, kcClsx, displayableErrors, i18n, valueOrValues } = props;
const { advancedMsg } = i18n;
@ -618,14 +604,14 @@ function SelectTag(props: InputFiledByTypeProps) {
<select
id={attribute.name}
name={attribute.name}
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
aria-invalid={displayableErrors.length !== 0}
disabled={attribute.readOnly}
multiple={isMultiple}
size={attribute.annotations.inputTypeSize === undefined ? undefined : parseInt(`${attribute.annotations.inputTypeSize}`)}
value={valueOrValues}
onChange={event =>
formValidationDispatch({
dispatchFormAction({
action: "update",
name: attribute.name,
valueOrValues: (() => {
@ -638,7 +624,7 @@ function SelectTag(props: InputFiledByTypeProps) {
})
}
onBlur={() =>
formValidationDispatch({
dispatchFormAction({
action: "focus lost",
name: attribute.name,
fieldIndex: undefined

View File

@ -1,4 +1,4 @@
export type { MessageKey } from "./i18n";
export type { MessageKey, KcContextLike } from "./i18n";
import { createUseI18n } from "./i18n";
export { createUseI18n };
export { fallbackLanguageTag } from "./i18n";

View File

@ -1,7 +1,7 @@
import { createUseClassName } from "keycloakify/lib/useGetClassName";
import { createGetKcClsx } from "keycloakify/lib/getKcClsx";
import type { ClassKey } from "keycloakify/login/TemplateProps";
export const { useGetClassName } = createUseClassName<ClassKey>({
export const { getKcClsx } = createGetKcClsx<ClassKey>({
defaultClasses: {
kcHtmlClass: "login-pf",
kcBodyClass: undefined,
@ -137,3 +137,7 @@ export const { useGetClassName } = createUseClassName<ClassKey>({
kcLabelClass: "pf-c-form__label pf-c-form__label-text"
}
});
export type { ClassKey };
export type KcClsx = ReturnType<typeof getKcClsx>["kcClsx"];

View File

@ -7,9 +7,11 @@ import { useConstCallback } from "keycloakify/tools/useConstCallback";
import { emailRegexp } from "keycloakify/tools/emailRegExp";
import { formatNumber } from "keycloakify/tools/formatNumber";
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
import type { KcContext, PasswordPolicies, Attribute, Validators } from "keycloakify/login/KcContext";
import type { PasswordPolicies, Attribute, Validators } from "keycloakify/login/KcContext";
import type { KcContext } from "../KcContext";
import type { MessageKey } from "keycloakify/login/i18n";
import type { I18n } from "../i18n";
import { KcContextLike as KcContextLike_i18n } from "keycloakify/login/i18n";
import { useI18n } from "../i18n";
export type FormFieldError = {
errorMessage: JSX.Element;
@ -64,23 +66,23 @@ export type FormAction =
fieldIndex: number | undefined;
};
export type KcContextLike = {
messagesPerField: Pick<KcContext.Common["messagesPerField"], "existsError" | "get">;
export type KcContextLike = KcContextLike_i18n &
KcContextLike_useGetErrors & {
profile: {
attributesByName: Record<string, Attribute>;
html5DataAnnotations?: Record<string, string>;
};
passwordRequired?: boolean;
realm: { registrationEmailAsUsername: boolean };
passwordPolicies?: PasswordPolicies;
url: {
resourcesPath: string;
};
};
assert<Extract<KcContext.Register, { pageId: "register.ftl" }> extends KcContextLike ? true : false>();
export type ParamsOfUseUserProfileForm = {
kcContext: KcContextLike;
i18n: I18n;
doMakeUserConfirmPassword: boolean;
};
@ -103,7 +105,7 @@ namespace internal {
}
export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTypeOfUseUserProfileForm {
const { kcContext, i18n, doMakeUserConfirmPassword } = params;
const { kcContext, doMakeUserConfirmPassword } = params;
const { insertScriptTags } = useInsertScriptTags({
componentOrHookName: "useUserProfileForm",
@ -120,8 +122,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
}, []);
const { getErrors } = useGetErrors({
kcContext,
i18n
kcContext
});
const initialState = useMemo((): internal.State => {
@ -515,12 +516,19 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
};
}
function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField" | "passwordPolicies">; i18n: I18n }) {
const { kcContext, i18n } = params;
type KcContextLike_useGetErrors = KcContextLike_i18n & {
messagesPerField: Pick<KcContext["messagesPerField"], "existsError" | "get">;
passwordPolicies?: PasswordPolicies;
};
assert<KcContextLike extends KcContextLike_useGetErrors ? true : false>();
function useGetErrors(params: { kcContext: KcContextLike_useGetErrors }) {
const { kcContext } = params;
const { messagesPerField, passwordPolicies } = kcContext;
const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;
const { msg, msgStr, advancedMsg, advancedMsgStr } = useI18n({ kcContext });
const getErrors = useConstCallback(
(params: {

View File

@ -1,4 +1,4 @@
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -6,7 +6,7 @@ import { useI18n } from "../i18n";
export default function Code(props: PageProps<Extract<KcContext, { pageId: "code.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -17,14 +17,16 @@ export default function Code(props: PageProps<Extract<KcContext, { pageId: "code
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
headerNode={code.success ? msg("codeSuccessTitle") : msg("codeErrorTitle", code.error)}
>
<div id="kc-code">
{code.success ? (
<>
<p>{msg("copyCodeInstruction")}</p>
<input id="code" className={getClassName("kcTextareaClass")} defaultValue={code.code} />
<input id="code" className={kcClsx("kcTextareaClass")} defaultValue={code.code} />
</>
) : (
<p id="error">{code.error}</p>

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n } from "../i18n";
export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext, { pageId: "delete-account-confirm.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -17,7 +16,7 @@ export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext,
const { msg, msgStr } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("deleteAccountConfirm")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("deleteAccountConfirm")}>
<form action={url.loginAction} className="form-vertical" method="post">
<div className="alert alert-warning" style={{ marginTop: "0", marginBottom: "30px" }}>
<span className="pficon pficon-warning-triangle-o"></span>
@ -37,13 +36,13 @@ export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext,
<p className="delete-account-text">{msg("finalDeletionConfirmation")}</p>
<div id="kc-form-buttons">
<input
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonPrimaryClass"), getClassName("kcButtonLargeClass"))}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
type="submit"
value={msgStr("doConfirmDelete")}
/>
{triggered_from_aia && (
<button
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
style={{ marginLeft: "calc(100% - 220px)" }}
type="submit"
name="cancel-aia"

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -9,7 +8,7 @@ export default function DeleteCredential(props: PageProps<Extract<KcContext, { p
const { msgStr, msg } = useI18n({ kcContext });
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -17,18 +16,24 @@ export default function DeleteCredential(props: PageProps<Extract<KcContext, { p
const { url, credentialLabel } = kcContext;
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("deleteCredentialTitle", credentialLabel)}>
<Template
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={false}
headerNode={msg("deleteCredentialTitle", credentialLabel)}
>
<div id="kc-delete-text">{msg("deleteCredentialMessage", credentialLabel)}</div>
<form className="form-actions" action={url.loginAction} method="POST">
<input
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonPrimaryClass"), getClassName("kcButtonLargeClass"))}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
name="accept"
id="kc-accept"
type="submit"
value={msgStr("doConfirmDelete")}
/>
<input
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
name="cancel-aia"
value={msgStr("doCancel")}
id="kc-decline"

View File

@ -10,7 +10,7 @@ export default function Error(props: PageProps<Extract<KcContext, { pageId: "err
const { msg } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("errorTitle")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} displayMessage={false} headerNode={msg("errorTitle")}>
<div id="kc-error-message">
<p className="instruction">{message.summary}</p>
{!skipLink && client !== undefined && client.baseUrl !== undefined && (

View File

@ -18,7 +18,9 @@ export default function FrontchannelLogout(props: PageProps<Extract<KcContext, {
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
documentTitle={msgStr("frontchannel-logout.title")}
headerNode={msg("frontchannel-logout.title")}
>

View File

@ -1,7 +1,6 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx";
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
import type { KcContext } from "../KcContext";
@ -14,7 +13,7 @@ type IdpReviewUserProfileProps = PageProps<Extract<KcContext, { pageId: "idp-rev
export default function IdpReviewUserProfile(props: IdpReviewUserProfileProps) {
const { kcContext, doUseDefaultCss, Template, classes, UserProfileFormFields } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -27,25 +26,22 @@ export default function IdpReviewUserProfile(props: IdpReviewUserProfileProps) {
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={messagesPerField.exists("global")}
displayRequiredFields
headerNode={msg("loginIdpReviewProfileTitle")}
>
<form id="kc-idp-review-profile-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} getClassName={getClassName} />
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")} />
<form id="kc-idp-review-profile-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} kcClsx={kcClsx} />
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")} />
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
type="submit"
value={msgStr("doSubmit")}
disabled={!isFomSubmittable}

View File

@ -17,7 +17,9 @@ export default function Info(props: PageProps<Extract<KcContext, { pageId: "info
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={false}
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
>

View File

@ -2,14 +2,14 @@ import { useState, useEffect, useReducer } from "react";
import { assert } from "tsafe/assert";
import { clsx } from "keycloakify/tools/clsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
import type { KcContext } from "../KcContext";
import { useI18n, type I18n } from "../i18n";
export default function Login(props: PageProps<Extract<KcContext, { pageId: "login.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -23,7 +23,9 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={!messagesPerField.existsError("username", "password")}
headerNode={msg("loginAccountTitle")}
displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
@ -42,32 +44,23 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
socialProvidersNode={
<>
{realm.password && social.providers?.length && (
<div id="kc-social-providers" className={getClassName("kcFormSocialAccountSectionClass")}>
<div id="kc-social-providers" className={kcClsx("kcFormSocialAccountSectionClass")}>
<hr />
<h2>{msg("identity-provider-login-label")}</h2>
<ul
className={clsx(
getClassName("kcFormSocialAccountListClass"),
social.providers.length > 3 && getClassName("kcFormSocialAccountListGridClass")
)}
>
<ul className={kcClsx("kcFormSocialAccountListClass", social.providers.length > 3 && "kcFormSocialAccountListGridClass")}>
{social.providers.map((...[p, , providers]) => (
<li key={p.alias}>
<a
id={`social-${p.alias}`}
className={clsx(
getClassName("kcFormSocialAccountListButtonClass"),
providers.length > 3 && getClassName("kcFormSocialAccountGridItem")
className={kcClsx(
"kcFormSocialAccountListButtonClass",
providers.length > 3 && "kcFormSocialAccountGridItem"
)}
type="button"
href={p.loginUrl}
>
{p.iconClasses && (
<i className={clsx(getClassName("kcCommonLogoIdP"), p.iconClasses)} aria-hidden="true"></i>
)}
<span
className={clsx(getClassName("kcFormSocialAccountNameClass"), p.iconClasses && "kc-social-icon-text")}
>
{p.iconClasses && <i className={clsx(kcClsx("kcCommonLogoIdP"), p.iconClasses)} aria-hidden="true"></i>}
<span className={clsx(kcClsx("kcFormSocialAccountNameClass"), p.iconClasses && "kc-social-icon-text")}>
{p.displayName}
</span>
</a>
@ -92,8 +85,8 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
method="post"
>
{!usernameHidden && (
<div className={getClassName("kcFormGroupClass")}>
<label htmlFor="username" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<label htmlFor="username" className={kcClsx("kcLabelClass")}>
{!realm.loginWithEmailAllowed
? msg("username")
: !realm.registrationEmailAsUsername
@ -103,7 +96,7 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
<input
tabIndex={2}
id="username"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
name="username"
defaultValue={login.username ?? ""}
type="text"
@ -112,22 +105,22 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
aria-invalid={messagesPerField.existsError("username", "password")}
/>
{messagesPerField.existsError("username", "password") && (
<span id="input-error" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.getFirstError("username", "password")}
</span>
)}
</div>
)}
<div className={getClassName("kcFormGroupClass")}>
<label htmlFor="password" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<label htmlFor="password" className={kcClsx("kcLabelClass")}>
{msg("password")}
</label>
<PasswordWrapper getClassName={getClassName} i18n={i18n} passwordInputId="password">
<PasswordWrapper kcClsx={kcClsx} i18n={i18n} passwordInputId="password">
<input
tabIndex={3}
id="password"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
name="password"
type="password"
autoComplete="current-password"
@ -135,13 +128,13 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
/>
</PasswordWrapper>
{usernameHidden && messagesPerField.existsError("username", "password") && (
<span id="input-error" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.getFirstError("username", "password")}
</span>
)}
</div>
<div className={clsx(getClassName("kcFormGroupClass"), getClassName("kcFormSettingClass"))}>
<div className={kcClsx("kcFormGroupClass", "kcFormSettingClass")}>
<div id="kc-form-options">
{realm.rememberMe && !usernameHidden && (
<div className="checkbox">
@ -158,7 +151,7 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
</div>
)}
</div>
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
{realm.resetPasswordAllowed && (
<span>
<a tabIndex={6} href={url.loginResetCredentialsUrl}>
@ -169,17 +162,12 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
</div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormGroupClass")}>
<input type="hidden" id="id-hidden-input" name="credentialId" value={auth.selectedCredential} />
<input
tabIndex={7}
disabled={isLoginButtonDisabled}
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="login"
id="kc-login"
type="submit"
@ -194,13 +182,8 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
);
}
function PasswordWrapper(props: {
getClassName: ReturnType<typeof useGetClassName>["getClassName"];
i18n: I18n;
passwordInputId: string;
children: JSX.Element;
}) {
const { getClassName, i18n, passwordInputId, children } = props;
function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: string; children: JSX.Element }) {
const { kcClsx, i18n, passwordInputId, children } = props;
const { msgStr } = i18n;
@ -215,19 +198,16 @@ function PasswordWrapper(props: {
}, [isPasswordRevealed]);
return (
<div className={getClassName("kcInputGroup")}>
<div className={kcClsx("kcInputGroup")}>
{children}
<button
type="button"
className={getClassName("kcFormPasswordVisibilityButtonClass")}
className={kcClsx("kcFormPasswordVisibilityButtonClass")}
aria-label={msgStr(isPasswordRevealed ? "hidePassword" : "showPassword")}
aria-controls={passwordInputId}
onClick={toggleIsPasswordRevealed}
>
<i
className={getClassName(isPasswordRevealed ? "kcFormPasswordVisibilityIconHide" : "kcFormPasswordVisibilityIconShow")}
aria-hidden
/>
<i className={kcClsx(isPasswordRevealed ? "kcFormPasswordVisibilityIconHide" : "kcFormPasswordVisibilityIconShow")} aria-hidden />
</button>
</div>
);

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName, type ClassKey } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx, KcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n, type I18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n, type I18n } from "../i18n";
export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pageId: "login-config-totp.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -19,7 +18,7 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
const { msg, msgStr, advancedMsg } = i18n;
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("loginTotpTitle")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("loginTotpTitle")}>
<>
<ol id="kc-totp-settings">
<li>
@ -89,26 +88,26 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
</li>
</ol>
<form action={url.loginAction} className={getClassName("kcFormClass")} id="kc-totp-settings-form" method="post">
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcInputWrapperClass")}>
<label htmlFor="totp" className={getClassName("kcLabelClass")}>
<form action={url.loginAction} className={kcClsx("kcFormClass")} id="kc-totp-settings-form" method="post">
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<label htmlFor="totp" className={kcClsx("kcLabelClass")}>
{msg("authenticatorCode")}
</label>{" "}
<span className="required">*</span>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<input
type="text"
id="totp"
name="totp"
autoComplete="off"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
aria-invalid={messagesPerField.existsError("totp")}
/>
{messagesPerField.existsError("totp") && (
<span id="input-error-otp-code" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-otp-code" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("totp")}
</span>
)}
@ -117,54 +116,45 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
{mode && <input type="hidden" id="mode" value={mode} />}
</div>
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcInputWrapperClass")}>
<label htmlFor="userLabel" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<label htmlFor="userLabel" className={kcClsx("kcLabelClass")}>
{msg("loginTotpDeviceName")}
</label>{" "}
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
</div>
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<input
type="text"
id="userLabel"
name="userLabel"
autoComplete="off"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
aria-invalid={messagesPerField.existsError("userLabel")}
/>
{messagesPerField.existsError("userLabel") && (
<span id="input-error-otp-label" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-otp-label" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("userLabel")}
</span>
)}
</div>
</div>
<div className={getClassName("kcFormGroupClass")}>
<LogoutOtherSessions {...{ getClassName, i18n }} />
<div className={kcClsx("kcFormGroupClass")}>
<LogoutOtherSessions kcClsx={kcClsx} i18n={i18n} />
</div>
{isAppInitiatedAction ? (
<>
<input
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
id="saveTOTPBtn"
value={msgStr("doSubmit")}
/>
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass", "kcButtonLargeClass")}
id="cancelTOTPBtn"
name="cancel-aia"
value="true"
@ -175,7 +165,7 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
) : (
<input
type="submit"
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonPrimaryClass"), getClassName("kcButtonLargeClass"))}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
id="saveTOTPBtn"
value={msgStr("doSubmit")}
/>
@ -186,14 +176,14 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
);
}
function LogoutOtherSessions(props: { getClassName: (key: ClassKey) => string; i18n: I18n }) {
const { getClassName, i18n } = props;
function LogoutOtherSessions(props: { kcClsx: KcClsx; i18n: I18n }) {
const { kcClsx, i18n } = props;
const { msg } = i18n;
return (
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
<div className="checkbox">
<label>
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" defaultChecked={true} />

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n } from "../i18n";
export default function LoginIdpLinkConfirm(props: PageProps<Extract<KcContext, { pageId: "login-idp-link-confirm.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -17,17 +16,12 @@ export default function LoginIdpLinkConfirm(props: PageProps<Extract<KcContext,
const { msg } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("confirmLinkIdpTitle")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("confirmLinkIdpTitle")}>
<form id="kc-register-form" action={url.loginAction} method="post">
<div className={getClassName("kcFormGroupClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="submitAction"
id="updateProfile"
value="updateProfile"
@ -36,12 +30,7 @@ export default function LoginIdpLinkConfirm(props: PageProps<Extract<KcContext,
</button>
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="submitAction"
id="linkAccount"
value="linkAccount"

View File

@ -10,7 +10,7 @@ export default function LoginIdpLinkEmail(props: PageProps<Extract<KcContext, {
const { msg } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("emailLinkIdpTitle", idpAlias)}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("emailLinkIdpTitle", idpAlias)}>
<p id="instruction1" className="instruction">
{msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.displayName)}
</p>

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import { PageProps } from "keycloakify/login/pages/PageProps";
import { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -12,51 +11,47 @@ export default function LoginOauth2DeviceVerifyUserCode(
const { msg, msgStr } = useI18n({ kcContext });
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("oauth2DeviceVerificationTitle")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("oauth2DeviceVerificationTitle")}>
<form
id="kc-user-verify-device-user-code-form"
className={getClassName("kcFormClass")}
className={kcClsx("kcFormClass")}
action={url.oauth2DeviceVerificationAction}
method="post"
>
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="device-user-code" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor="device-user-code" className={kcClsx("kcLabelClass")}>
{msg("verifyOAuth2DeviceUserCode")}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<input
id="device-user-code"
name="device_user_code"
autoComplete="off"
type="text"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
autoFocus
/>
</div>
</div>
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}></div>
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}></div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div className={getClassName("kcFormButtonsWrapperClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<div className={kcClsx("kcFormButtonsWrapperClass")}>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
type="submit"
value={msgStr("doSubmit")}
/>

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import { PageProps } from "keycloakify/login/pages/PageProps";
import { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -10,14 +9,16 @@ export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pa
const { msg, msgStr, advancedMsg, advancedMsgStr } = useI18n({ kcContext });
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
bodyClassName="oauth"
headerNode={
<>
@ -68,30 +69,22 @@ export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pa
<form className="form-actions" action={url.oauthAction} method="POST">
<input type="hidden" name="code" value={oauth.code} />
<div className={getClassName("kcFormGroupClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options">
<div className={getClassName("kcFormOptionsWrapperClass")}></div>
<div className={kcClsx("kcFormOptionsWrapperClass")}></div>
</div>
<div id="kc-form-buttons">
<div className={getClassName("kcFormButtonsWrapperClass")}>
<div className={kcClsx("kcFormButtonsWrapperClass")}>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
name="accept"
id="kc-login"
type="submit"
value={msgStr("doYes")}
/>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
name="cancel"
id="kc-cancel"
type="submit"

View File

@ -1,6 +1,5 @@
import { Fragment } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -8,7 +7,7 @@ import { useI18n } from "../i18n";
export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "login-otp.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -18,27 +17,33 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
const { msg, msgStr } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} displayMessage={!messagesPerField.existsError("totp")} headerNode={msg("doLogIn")}>
<form id="kc-otp-login-form" className={clsx(getClassName("kcFormClass"))} action={url.loginAction} method="post">
<Template
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={!messagesPerField.existsError("totp")}
headerNode={msg("doLogIn")}
>
<form id="kc-otp-login-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
{otpLogin.userOtpCredentials.length > 1 && (
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
{otpLogin.userOtpCredentials.map((otpCredential, index) => (
<Fragment key={index}>
<input
id={`kc-otp-credential-${index}`}
className={getClassName("kcLoginOTPListInputClass")}
className={kcClsx("kcLoginOTPListInputClass")}
type="radio"
name="selectedCredentialId"
value={otpCredential.id}
defaultChecked={otpCredential.id === otpLogin.selectedCredentialId}
/>
<label htmlFor={`kc-otp-credential-${index}`} className={getClassName("kcLoginOTPListClass")} tabIndex={index}>
<span className={getClassName("kcLoginOTPListItemHeaderClass")}>
<span className={getClassName("kcLoginOTPListItemIconBodyClass")}>
<i className={getClassName("kcLoginOTPListItemIconClass")} aria-hidden="true"></i>
<label htmlFor={`kc-otp-credential-${index}`} className={kcClsx("kcLoginOTPListClass")} tabIndex={index}>
<span className={kcClsx("kcLoginOTPListItemHeaderClass")}>
<span className={kcClsx("kcLoginOTPListItemIconBodyClass")}>
<i className={kcClsx("kcLoginOTPListItemIconClass")} aria-hidden="true"></i>
</span>
<span className={getClassName("kcLoginOTPListItemTitleClass")}>{otpCredential.userLabel}</span>
<span className={kcClsx("kcLoginOTPListItemTitleClass")}>{otpCredential.userLabel}</span>
</span>
</label>
</Fragment>
@ -47,42 +52,37 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
</div>
)}
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="otp" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor="otp" className={kcClsx("kcLabelClass")}>
{msg("loginOtpOneTime")}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<input
id="otp"
name="otp"
autoComplete="off"
type="text"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
autoFocus
aria-invalid={messagesPerField.existsError("totp")}
/>
{messagesPerField.existsError("totp") && (
<span id="input-error-otp-code" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-otp-code" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("totp")}
</span>
)}
</div>
</div>
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}></div>
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}></div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="login"
id="kc-login"
type="submit"

View File

@ -10,7 +10,7 @@ export default function LoginPageExpired(props: PageProps<Extract<KcContext, { p
const { msg } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("pageExpiredTitle")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("pageExpiredTitle")}>
<p id="instruction1" className="instruction">
{msg("pageExpiredMsg1")}
<a id="loginRestartLink" href={url.loginRestartFlowUrl}>

View File

@ -1,7 +1,7 @@
import { useState, useEffect, useReducer } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { assert } from "tsafe/assert";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n, type I18n } from "../i18n";
@ -9,7 +9,7 @@ import { useI18n, type I18n } from "../i18n";
export default function LoginPassword(props: PageProps<Extract<KcContext, { pageId: "login-password.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -22,7 +22,13 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("doLogIn")} displayMessage={!messagesPerField.existsError("password")}>
<Template
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
headerNode={msg("doLogIn")}
displayMessage={!messagesPerField.existsError("password")}
>
<div id="kc-form">
<div id="kc-form-wrapper">
<form
@ -34,17 +40,17 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
action={url.loginAction}
method="post"
>
<div className={clsx(getClassName("kcFormGroupClass"), "no-bottom-margin")}>
<div className={clsx(kcClsx("kcFormGroupClass"), "no-bottom-margin")}>
<hr />
<label htmlFor="password" className={getClassName("kcLabelClass")}>
<label htmlFor="password" className={kcClsx("kcLabelClass")}>
{msg("password")}
</label>
<PasswordWrapper getClassName={getClassName} i18n={i18n} passwordInputId="password">
<PasswordWrapper kcClsx={kcClsx} i18n={i18n} passwordInputId="password">
<input
tabIndex={2}
id="password"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
name="password"
type="password"
autoFocus
@ -54,14 +60,14 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
</PasswordWrapper>
{messagesPerField.existsError("password") && (
<span id="input-error-password" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-password" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("password")}
</span>
)}
</div>
<div className={clsx(getClassName("kcFormGroupClass"), getClassName("kcFormSettingClass"))}>
<div className={kcClsx("kcFormGroupClass", "kcFormSettingClass")}>
<div id="kc-form-options" />
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
{realm.resetPasswordAllowed && (
<span>
<a tabIndex={5} href={url.loginResetCredentialsUrl}>
@ -71,15 +77,10 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
)}
</div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormGroupClass")}>
<input
tabIndex={4}
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="login"
id="kc-login"
type="submit"
@ -94,13 +95,8 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
);
}
function PasswordWrapper(props: {
getClassName: ReturnType<typeof useGetClassName>["getClassName"];
i18n: I18n;
passwordInputId: string;
children: JSX.Element;
}) {
const { getClassName, i18n, passwordInputId, children } = props;
function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: string; children: JSX.Element }) {
const { kcClsx, i18n, passwordInputId, children } = props;
const { msgStr } = i18n;
@ -115,19 +111,16 @@ function PasswordWrapper(props: {
}, [isPasswordRevealed]);
return (
<div className={getClassName("kcInputGroup")}>
<div className={kcClsx("kcInputGroup")}>
{children}
<button
type="button"
className={getClassName("kcFormPasswordVisibilityButtonClass")}
className={kcClsx("kcFormPasswordVisibilityButtonClass")}
aria-label={msgStr(isPasswordRevealed ? "hidePassword" : "showPassword")}
aria-controls={passwordInputId}
onClick={toggleIsPasswordRevealed}
>
<i
className={getClassName(isPasswordRevealed ? "kcFormPasswordVisibilityIconHide" : "kcFormPasswordVisibilityIconShow")}
aria-hidden
/>
<i className={kcClsx(isPasswordRevealed ? "kcFormPasswordVisibilityIconHide" : "kcFormPasswordVisibilityIconShow")} aria-hidden />
</button>
</div>
);

View File

@ -1,6 +1,6 @@
import { useEffect } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
@ -9,7 +9,7 @@ import { useI18n, type I18n } from "../i18n";
export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<KcContext, { pageId: "login-recovery-authn-code-config.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -145,8 +145,8 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
}, []);
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("recovery-code-config-header")}>
<div className={clsx("pf-c-alert", "pf-m-warning", "pf-m-inline", getClassName("kcRecoveryCodesWarning"))} aria-label="Warning alert">
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("recovery-code-config-header")}>
<div className={clsx("pf-c-alert", "pf-m-warning", "pf-m-inline", kcClsx("kcRecoveryCodesWarning"))} aria-label="Warning alert">
<div className="pf-c-alert__icon">
<i className="pficon-warning-triangle-o" aria-hidden="true" />
</div>
@ -159,7 +159,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
</div>
</div>
<ol id="kc-recovery-codes-list" className={getClassName("kcRecoveryCodesList")}>
<ol id="kc-recovery-codes-list" className={kcClsx("kcRecoveryCodesList")}>
{recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesList.map((code, index) => (
<li key={index}>
<span>{index + 1}:</span> {code.slice(0, 4)}-{code.slice(4, 8)}-{code.slice(8)}
@ -168,7 +168,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
</ol>
{/* actions */}
<div className={getClassName("kcRecoveryCodesActions")}>
<div className={kcClsx("kcRecoveryCodesActions")}>
<button id="printRecoveryCodes" className={clsx("pf-c-button", "pf-m-link")} type="button">
<i className="pficon-print" aria-hidden="true" /> {msg("recovery-codes-print")}
</button>
@ -181,9 +181,9 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
</div>
{/* confirmation checkbox */}
<div className={getClassName("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsClass")}>
<input
className={getClassName("kcCheckInputClass")}
className={kcClsx("kcCheckInputClass")}
type="checkbox"
id="kcRecoveryCodesConfirmationCheck"
name="kcRecoveryCodesConfirmationCheck"
@ -195,25 +195,25 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
<label htmlFor="kcRecoveryCodesConfirmationCheck">{msg("recovery-codes-confirmation-message")}</label>
</div>
<form action={kcContext.url.loginAction} className={getClassName("kcFormGroupClass")} id="kc-recovery-codes-settings-form" method="post">
<form action={kcContext.url.loginAction} className={kcClsx("kcFormGroupClass")} id="kc-recovery-codes-settings-form" method="post">
<input type="hidden" name="generatedRecoveryAuthnCodes" value={recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesAsString} />
<input type="hidden" name="generatedAt" value={recoveryAuthnCodesConfigBean.generatedAt} />
<input type="hidden" id="userLabel" name="userLabel" value={msgStr("recovery-codes-label-default")} />
<LogoutOtherSessions {...{ getClassName, i18n }} />
<LogoutOtherSessions kcClsx={kcClsx} i18n={i18n} />
{isAppInitiatedAction ? (
<>
<input
type="submit"
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonPrimaryClass"), getClassName("kcButtonLargeClass"))}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
id="saveRecoveryAuthnCodesBtn"
value={msgStr("recovery-codes-action-complete")}
disabled
/>
<button
type="submit"
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
id="cancelRecoveryAuthnCodesBtn"
name="cancel-aia"
value="true"
@ -224,12 +224,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
) : (
<input
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
id="saveRecoveryAuthnCodesBtn"
value={msgStr("recovery-codes-action-complete")}
disabled
@ -240,14 +235,14 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
);
}
function LogoutOtherSessions(props: { getClassName: ReturnType<typeof useGetClassName>["getClassName"]; i18n: I18n }) {
const { getClassName, i18n } = props;
function LogoutOtherSessions(props: { kcClsx: KcClsx; i18n: I18n }) {
const { kcClsx, i18n } = props;
const { msg } = i18n;
return (
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
<div className="checkbox">
<label>
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" defaultChecked={true} />

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n } from "../i18n";
export default function LoginRecoveryAuthnCodeInput(props: PageProps<Extract<KcContext, { pageId: "login-recovery-authn-code-input.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -18,18 +17,20 @@ export default function LoginRecoveryAuthnCodeInput(props: PageProps<Extract<KcC
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
headerNode={msg("auth-recovery-code-header")}
displayMessage={!messagesPerField.existsError("recoveryCodeInput")}
>
<form id="kc-recovery-code-login-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="recoveryCodeInput" className={getClassName("kcLabelClass")}>
<form id="kc-recovery-code-login-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor="recoveryCodeInput" className={kcClsx("kcLabelClass")}>
{msg("auth-recovery-code-prompt", `${recoveryAuthnCodesInputBean.codeNumber}`)}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<input
tabIndex={1}
id="recoveryCodeInput"
@ -37,29 +38,24 @@ export default function LoginRecoveryAuthnCodeInput(props: PageProps<Extract<KcC
aria-invalid={messagesPerField.existsError("recoveryCodeInput")}
autoComplete="off"
type="text"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
autoFocus
/>
{messagesPerField.existsError("recoveryCodeInput") && (
<span id="input-error" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("recoveryCodeInput")}
</span>
)}
</div>
</div>
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsWrapperClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")} />
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsWrapperClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")} />
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="login"
id="kc-login"
type="submit"

View File

@ -1,6 +1,5 @@
import { Fragment } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -8,7 +7,7 @@ import { useI18n } from "../i18n";
export default function LoginResetOtp(props: PageProps<Extract<KcContext, { pageId: "login-reset-otp.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -18,41 +17,42 @@ export default function LoginResetOtp(props: PageProps<Extract<KcContext, { page
const { msg, msgStr } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} displayMessage={!messagesPerField.existsError("totp")} headerNode={msg("doLogIn")}>
<form id="kc-otp-reset-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<div className={getClassName("kcInputWrapperClass")}>
<div className={getClassName("kcInfoAreaWrapperClass")}>
<Template
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={!messagesPerField.existsError("totp")}
headerNode={msg("doLogIn")}
>
<form id="kc-otp-reset-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<div className={kcClsx("kcInputWrapperClass")}>
<div className={kcClsx("kcInfoAreaWrapperClass")}>
<p id="kc-otp-reset-form-description">{msg("otp-reset-description")}</p>
{configuredOtpCredentials.userOtpCredentials.map((otpCredential, index) => (
<Fragment key={otpCredential.id}>
<input
id={`kc-otp-credential-${index}`}
className={getClassName("kcLoginOTPListInputClass")}
className={kcClsx("kcLoginOTPListInputClass")}
type="radio"
name="selectedCredentialId"
value={otpCredential.id}
defaultChecked={otpCredential.id === configuredOtpCredentials.selectedCredentialId}
/>
<label htmlFor={`kc-otp-credential-${index}`} className={getClassName("kcLoginOTPListClass")} tabIndex={index}>
<span className={getClassName("kcLoginOTPListItemHeaderClass")}>
<span className={getClassName("kcLoginOTPListItemIconBodyClass")}>
<i className={getClassName("kcLoginOTPListItemIconClass")} aria-hidden="true"></i>
<label htmlFor={`kc-otp-credential-${index}`} className={kcClsx("kcLoginOTPListClass")} tabIndex={index}>
<span className={kcClsx("kcLoginOTPListItemHeaderClass")}>
<span className={kcClsx("kcLoginOTPListItemIconBodyClass")}>
<i className={kcClsx("kcLoginOTPListItemIconClass")} aria-hidden="true"></i>
</span>
<span className={getClassName("kcLoginOTPListItemTitleClass")}>{otpCredential.userLabel}</span>
<span className={kcClsx("kcLoginOTPListItemTitleClass")}>{otpCredential.userLabel}</span>
</span>
</label>
</Fragment>
))}
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
id="kc-otp-reset-form-submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
type="submit"
value={msgStr("doSubmit")}
/>

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n } from "../i18n";
export default function LoginResetPassword(props: PageProps<Extract<KcContext, { pageId: "login-reset-password.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -18,16 +17,18 @@ export default function LoginResetPassword(props: PageProps<Extract<KcContext, {
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayInfo
displayMessage={!messagesPerField.existsError("username")}
infoNode={realm.duplicateEmailsAllowed ? msg("emailInstructionUsername") : msg("emailInstruction")}
headerNode={msg("emailForgotTitle")}
>
<form id="kc-reset-password-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="username" className={getClassName("kcLabelClass")}>
<form id="kc-reset-password-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor="username" className={kcClsx("kcLabelClass")}>
{!realm.loginWithEmailAllowed
? msg("username")
: !realm.registrationEmailAsUsername
@ -35,40 +36,35 @@ export default function LoginResetPassword(props: PageProps<Extract<KcContext, {
: msg("email")}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<input
type="text"
id="username"
name="username"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
autoFocus
defaultValue={auth.attemptedUsername ?? ""}
aria-invalid={messagesPerField.existsError("username")}
/>
{messagesPerField.existsError("username") && (
<span id="input-error-username" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-username" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("username")}
</span>
)}
</div>
</div>
<div className={clsx(getClassName("kcFormGroupClass"), getClassName("kcFormSettingClass"))}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div className={kcClsx("kcFormGroupClass", "kcFormSettingClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
type="submit"
value={msgStr("doSubmit")}
/>

View File

@ -1,7 +1,6 @@
import { useEffect, useReducer } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { assert } from "tsafe/assert";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n, type I18n } from "../i18n";
@ -9,7 +8,7 @@ import { useI18n, type I18n } from "../i18n";
export default function LoginUpdatePassword(props: PageProps<Extract<KcContext, { pageId: "login-update-password.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -21,23 +20,25 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={!messagesPerField.existsError("password", "password-confirm")}
headerNode={msg("updatePasswordTitle")}
>
<form id="kc-passwd-update-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="password-new" className={getClassName("kcLabelClass")}>
<form id="kc-passwd-update-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor="password-new" className={kcClsx("kcLabelClass")}>
{msg("passwordNew")}
</label>
<div className={getClassName("kcInputWrapperClass")}>
<PasswordWrapper {...{ getClassName, i18n }} passwordInputId="password-new">
<div className={kcClsx("kcInputWrapperClass")}>
<PasswordWrapper kcClsx={kcClsx} i18n={i18n} passwordInputId="password-new">
<input
type="password"
id="password-new"
name="password-new"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
autoFocus
autoComplete="new-password"
aria-invalid={messagesPerField.existsError("password", "password-confirm")}
@ -45,7 +46,7 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
</PasswordWrapper>
{messagesPerField.existsError("password") && (
<span id="input-error-password" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-password" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("password")}
</span>
)}
@ -53,19 +54,19 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
</div>
</div>
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="password-confirm" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor="password-confirm" className={kcClsx("kcLabelClass")}>
{msg("passwordConfirm")}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<PasswordWrapper {...{ getClassName, i18n }} passwordInputId="password-confirm">
<div className={kcClsx("kcInputWrapperClass")}>
<PasswordWrapper kcClsx={kcClsx} i18n={i18n} passwordInputId="password-confirm">
<input
type="password"
id="password-confirm"
name="password-confirm"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
autoFocus
autoComplete="new-password"
aria-invalid={messagesPerField.existsError("password", "password-confirm")}
@ -73,33 +74,29 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
</PasswordWrapper>
{messagesPerField.existsError("password-confirm") && (
<span id="input-error-password-confirm" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error-password-confirm" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("password-confirm")}
</span>
)}
</div>
<div className={getClassName("kcFormGroupClass")}>
<LogoutOtherSessions {...{ getClassName, i18n }} />
<div className={kcClsx("kcFormGroupClass")}>
<LogoutOtherSessions kcClsx={kcClsx} i18n={i18n} />
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
isAppInitiatedAction && getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
className={kcClsx(
"kcButtonClass",
"kcButtonPrimaryClass",
isAppInitiatedAction && "kcButtonBlockClass",
"kcButtonLargeClass"
)}
type="submit"
value={msgStr("doSubmit")}
/>
{isAppInitiatedAction && (
<button
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
type="submit"
name="cancel-aia"
value="true"
@ -115,14 +112,14 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
);
}
function LogoutOtherSessions(props: { getClassName: ReturnType<typeof useGetClassName>["getClassName"]; i18n: I18n }) {
const { getClassName, i18n } = props;
function LogoutOtherSessions(props: { kcClsx: KcClsx; i18n: I18n }) {
const { kcClsx, i18n } = props;
const { msg } = i18n;
return (
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
<div className="checkbox">
<label>
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" defaultChecked={true} />
@ -134,13 +131,8 @@ function LogoutOtherSessions(props: { getClassName: ReturnType<typeof useGetClas
);
}
function PasswordWrapper(props: {
getClassName: ReturnType<typeof useGetClassName>["getClassName"];
i18n: I18n;
passwordInputId: string;
children: JSX.Element;
}) {
const { getClassName, i18n, passwordInputId, children } = props;
function PasswordWrapper(props: { kcClsx: KcClsx; i18n: I18n; passwordInputId: string; children: JSX.Element }) {
const { kcClsx, i18n, passwordInputId, children } = props;
const { msgStr } = i18n;
@ -155,19 +147,16 @@ function PasswordWrapper(props: {
}, [isPasswordRevealed]);
return (
<div className={getClassName("kcInputGroup")}>
<div className={kcClsx("kcInputGroup")}>
{children}
<button
type="button"
className={getClassName("kcFormPasswordVisibilityButtonClass")}
className={kcClsx("kcFormPasswordVisibilityButtonClass")}
aria-label={msgStr(isPasswordRevealed ? "hidePassword" : "showPassword")}
aria-controls={passwordInputId}
onClick={toggleIsPasswordRevealed}
>
<i
className={getClassName(isPasswordRevealed ? "kcFormPasswordVisibilityIconHide" : "kcFormPasswordVisibilityIconShow")}
aria-hidden
/>
<i className={kcClsx(isPasswordRevealed ? "kcFormPasswordVisibilityIconHide" : "kcFormPasswordVisibilityIconShow")} aria-hidden />
</button>
</div>
);

View File

@ -1,7 +1,6 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx";
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
@ -14,51 +13,46 @@ type LoginUpdateProfileProps = PageProps<Extract<KcContext, { pageId: "login-upd
export default function LoginUpdateProfile(props: LoginUpdateProfileProps) {
const { kcContext, doUseDefaultCss, Template, classes, UserProfileFormFields } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
const { url, messagesPerField, isAppInitiatedAction } = kcContext;
const { url, isAppInitiatedAction } = kcContext;
const { msg, msgStr } = useI18n({ kcContext });
const [isFormSubmittable, setIsFormSubmittable] = useState(false);
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} displayRequiredFields headerNode={msg("loginProfileTitle")}>
<form id="kc-update-profile-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<UserProfileFormFields
{...{
kcContext,
getClassName,
messagesPerField
}}
onIsFormSubmittableValueChange={setIsFormSubmittable}
/>
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")} />
<Template
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayRequiredFields
headerNode={msg("loginProfileTitle")}
>
<form id="kc-update-profile-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<UserProfileFormFields kcContext={kcContext} kcClsx={kcClsx} onIsFormSubmittableValueChange={setIsFormSubmittable} />
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")} />
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
disabled={!isFormSubmittable}
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
!isAppInitiatedAction && getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
className={kcClsx(
"kcButtonClass",
"kcButtonPrimaryClass",
!isAppInitiatedAction && "kcButtonBlockClass",
"kcButtonLargeClass"
)}
type="submit"
value={msgStr("doSubmit")}
/>
{isAppInitiatedAction && (
<button
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
type="submit"
name="cancel-aia"
value="true"

View File

@ -1,6 +1,6 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -8,7 +8,7 @@ import { useI18n } from "../i18n";
export default function LoginUsername(props: PageProps<Extract<KcContext, { pageId: "login-username.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -21,7 +21,9 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={!messagesPerField.existsError("username")}
displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
infoNode={
@ -38,32 +40,23 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
socialProvidersNode={
<>
{realm.password && social.providers?.length && (
<div id="kc-social-providers" className={getClassName("kcFormSocialAccountSectionClass")}>
<div id="kc-social-providers" className={kcClsx("kcFormSocialAccountSectionClass")}>
<hr />
<h2>{msg("identity-provider-login-label")}</h2>
<ul
className={clsx(
getClassName("kcFormSocialAccountListClass"),
social.providers.length > 3 && getClassName("kcFormSocialAccountListGridClass")
)}
>
<ul className={kcClsx("kcFormSocialAccountListClass", social.providers.length > 3 && "kcFormSocialAccountListGridClass")}>
{social.providers.map((...[p, , providers]) => (
<li key={p.alias}>
<a
id={`social-${p.alias}`}
className={clsx(
getClassName("kcFormSocialAccountListButtonClass"),
providers.length > 3 && getClassName("kcFormSocialAccountGridItem")
className={kcClsx(
"kcFormSocialAccountListButtonClass",
providers.length > 3 && "kcFormSocialAccountGridItem"
)}
type="button"
href={p.loginUrl}
>
{p.iconClasses && (
<i className={clsx(getClassName("kcCommonLogoIdP"), p.iconClasses)} aria-hidden="true"></i>
)}
<span
className={clsx(getClassName("kcFormSocialAccountNameClass"), p.iconClasses && "kc-social-icon-text")}
>
{p.iconClasses && <i className={clsx(kcClsx("kcCommonLogoIdP"), p.iconClasses)} aria-hidden="true"></i>}
<span className={clsx(kcClsx("kcFormSocialAccountNameClass"), p.iconClasses && "kc-social-icon-text")}>
{p.displayName}
</span>
</a>
@ -88,8 +81,8 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
method="post"
>
{!usernameHidden && (
<div className={getClassName("kcFormGroupClass")}>
<label htmlFor="username" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<label htmlFor="username" className={kcClsx("kcLabelClass")}>
{!realm.loginWithEmailAllowed
? msg("username")
: !realm.registrationEmailAsUsername
@ -99,7 +92,7 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
<input
tabIndex={2}
id="username"
className={getClassName("kcInputClass")}
className={kcClsx("kcInputClass")}
name="username"
defaultValue={login.username ?? ""}
type="text"
@ -108,14 +101,14 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
aria-invalid={messagesPerField.existsError("username")}
/>
{messagesPerField.existsError("username") && (
<span id="input-error" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<span id="input-error" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.getFirstError("username")}
</span>
)}
</div>
)}
<div className={clsx(getClassName("kcFormGroupClass"), getClassName("kcFormSettingClass"))}>
<div className={kcClsx("kcFormGroupClass", "kcFormSettingClass")}>
<div id="kc-form-options">
{realm.rememberMe && !usernameHidden && (
<div className="checkbox">
@ -134,16 +127,11 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
</div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormGroupClass")}>
<input
tabIndex={4}
disabled={isLoginButtonDisabled}
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="login"
id="kc-login"
type="submit"

View File

@ -11,7 +11,9 @@ export default function LoginVerifyEmail(props: PageProps<Extract<KcContext, { p
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayInfo
headerNode={msg("emailVerifyTitle")}
infoNode={

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n } from "../i18n";
export default function LoginX509Info(props: PageProps<Extract<KcContext, { pageId: "login-x509-info.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -17,56 +16,52 @@ export default function LoginX509Info(props: PageProps<Extract<KcContext, { page
const { msg, msgStr } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("doLogIn")}>
<form id="kc-x509-login-info" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<div className={getClassName("kcFormGroupClass")}>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="certificate_subjectDN" className={getClassName("kcLabelClass")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("doLogIn")}>
<form id="kc-x509-login-info" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<div className={kcClsx("kcFormGroupClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor="certificate_subjectDN" className={kcClsx("kcLabelClass")}>
{msg("clientCertificate")}
</label>
</div>
{x509.formData.subjectDN ? (
<div className={getClassName("kcLabelWrapperClass")}>
<label id="certificate_subjectDN" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label id="certificate_subjectDN" className={kcClsx("kcLabelClass")}>
{x509.formData.subjectDN}
</label>
</div>
) : (
<div className={getClassName("kcLabelWrapperClass")}>
<label id="certificate_subjectDN" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label id="certificate_subjectDN" className={kcClsx("kcLabelClass")}>
{msg("noCertificate")}
</label>
</div>
)}
</div>
<div className={getClassName("kcFormGroupClass")}>
<div className={kcClsx("kcFormGroupClass")}>
{x509.formData.isUserEnabled && (
<>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="username" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label htmlFor="username" className={kcClsx("kcLabelClass")}>
{msg("doX509Login")}
</label>
</div>
<div className={getClassName("kcLabelWrapperClass")}>
<label id="username" className={getClassName("kcLabelClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<label id="username" className={kcClsx("kcLabelClass")}>
{x509.formData.username}
</label>
</div>
</>
)}
</div>
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")} />
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")} />
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div className={getClassName("kcFormButtonsWrapperClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<div className={kcClsx("kcFormButtonsWrapperClass")}>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
name="login"
id="kc-login"
type="submit"
@ -74,11 +69,7 @@ export default function LoginX509Info(props: PageProps<Extract<KcContext, { page
/>
{x509.formData.isUserEnabled && (
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
name="cancel"
id="kc-cancel"
type="submit"

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -7,7 +6,7 @@ import { useI18n } from "../i18n";
export default function LogoutConfirm(props: PageProps<Extract<KcContext, { pageId: "logout-confirm.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -17,24 +16,19 @@ export default function LogoutConfirm(props: PageProps<Extract<KcContext, { page
const { msg, msgStr } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("logoutConfirmTitle")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("logoutConfirmTitle")}>
<div id="kc-logout-confirm" className="content-area">
<p className="instruction">{msg("logoutConfirmHeader")}</p>
<form className="form-actions" action={url.logoutConfirmAction} method="POST">
<input type="hidden" name="session_code" value={logoutConfirm.code} />
<div className={getClassName("kcFormGroupClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options">
<div className={getClassName("kcFormOptionsWrapperClass")}></div>
<div className={kcClsx("kcFormOptionsWrapperClass")}></div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormGroupClass")}>
<input
tabIndex={4}
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="confirmLogout"
id="kc-logout"
type="submit"

View File

@ -1,9 +1,8 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { Markdown } from "keycloakify/tools/Markdown";
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
import { useTermsMarkdown } from "keycloakify/login/lib/useDownloadTerms";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
@ -16,7 +15,7 @@ type RegisterProps = PageProps<Extract<KcContext, { pageId: "register.ftl" }>> &
export default function Register(props: RegisterProps) {
const { kcContext, doUseDefaultCss, Template, classes, UserProfileFormFields } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -29,49 +28,29 @@ export default function Register(props: RegisterProps) {
const [isFormSubmittable, setIsFormSubmittable] = useState(false);
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("registerTitle")} displayRequiredFields>
<form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
<UserProfileFormFields
{...{
kcContext,
getClassName,
messagesPerField
}}
onIsFormSubmittableValueChange={setIsFormSubmittable}
/>
{termsAcceptanceRequired && (
<TermsAcceptance
{...{
i18n,
getClassName,
messagesPerField
}}
/>
)}
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("registerTitle")} displayRequiredFields>
<form id="kc-register-form" className={kcClsx("kcFormClass")} action={url.registrationAction} method="post">
<UserProfileFormFields kcContext={kcContext} kcClsx={kcClsx} onIsFormSubmittableValueChange={setIsFormSubmittable} />
{termsAcceptanceRequired && <TermsAcceptance i18n={i18n} kcClsx={kcClsx} messagesPerField={messagesPerField} />}
{recaptchaRequired && (
<div className="form-group">
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
</div>
</div>
)}
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
<span>
<a href={url.loginUrl}>{msg("backToLogin")}</a>
</span>
</div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
disabled={!isFormSubmittable}
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
type="submit"
value={msgStr("doRegister")}
/>
@ -82,12 +61,8 @@ export default function Register(props: RegisterProps) {
);
}
function TermsAcceptance(props: {
i18n: I18n;
getClassName: ReturnType<typeof useGetClassName>["getClassName"];
messagesPerField: Pick<KcContext["messagesPerField"], "existsError" | "get">;
}) {
const { i18n, getClassName, messagesPerField } = props;
function TermsAcceptance(props: { i18n: I18n; kcClsx: KcClsx; messagesPerField: Pick<KcContext["messagesPerField"], "existsError" | "get"> }) {
const { i18n, kcClsx, messagesPerField } = props;
const { msg } = i18n;
@ -101,7 +76,7 @@ function TermsAcceptance(props: {
return (
<>
<div className="form-group">
<div className={getClassName("kcInputWrapperClass")}>
<div className={kcClsx("kcInputWrapperClass")}>
{msg("termsTitle")}
<div id="kc-registration-terms-text">
<Markdown>{termsMarkdown}</Markdown>
@ -109,21 +84,21 @@ function TermsAcceptance(props: {
</div>
</div>
<div className="form-group">
<div className={getClassName("kcLabelWrapperClass")}>
<div className={kcClsx("kcLabelWrapperClass")}>
<input
type="checkbox"
id="termsAccepted"
name="termsAccepted"
className={getClassName("kcCheckboxInputClass")}
className={kcClsx("kcCheckboxInputClass")}
aria-invalid={messagesPerField.existsError("termsAccepted")}
/>
<label htmlFor="termsAccepted" className={getClassName("kcLabelClass")}>
<label htmlFor="termsAccepted" className={kcClsx("kcLabelClass")}>
{msg("acceptTerms")}
</label>
</div>
{messagesPerField.existsError("termsAccepted") && (
<div className={getClassName("kcLabelWrapperClass")}>
<span id="input-error-terms-accepted" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
<div className={kcClsx("kcLabelWrapperClass")}>
<span id="input-error-terms-accepted" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("termsAccepted")}
</span>
</div>

View File

@ -26,7 +26,7 @@ export default function SamlPostForm(props: PageProps<Extract<KcContext, { pageI
htmlFormElement.submit();
}, [htmlFormElement]);
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} headerNode={msg("saml.post-form.title")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("saml.post-form.title")}>
<p>{msg("saml.post-form.message")}</p>
<form name="saml-post-binding" method="post" action={samlPost.url} ref={setHtmlFormElement}>
{samlPost.SAMLRequest && <input type="hidden" name="SAMLRequest" value={samlPost.SAMLRequest} />}

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -8,36 +7,37 @@ export default function SelectAuthenticator(props: PageProps<Extract<KcContext,
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { url, auth } = kcContext;
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
const { msg } = useI18n({ kcContext });
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} displayInfo={false} headerNode={msg("loginChooseAuthenticator")}>
<form id="kc-select-credential-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<div className={getClassName("kcSelectAuthListClass")}>
<Template
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayInfo={false}
headerNode={msg("loginChooseAuthenticator")}
>
<form id="kc-select-credential-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<div className={kcClsx("kcSelectAuthListClass")}>
{auth.authenticationSelections.map((authenticationSelection, i) => (
<button
key={i}
className={getClassName("kcSelectAuthListItemClass")}
className={kcClsx("kcSelectAuthListItemClass")}
type="submit"
name="authenticationExecution"
value={authenticationSelection.authExecId}
>
<div className={getClassName("kcSelectAuthListItemIconClass")}>
<i
className={clsx(
getClassName(authenticationSelection.iconCssClass),
getClassName("kcSelectAuthListItemIconPropertyClass")
)}
/>
<div className={kcClsx("kcSelectAuthListItemIconClass")}>
<i className={kcClsx(authenticationSelection.iconCssClass, "kcSelectAuthListItemIconPropertyClass")} />
</div>
<div className={getClassName("kcSelectAuthListItemBodyClass")}>
<div className={getClassName("kcSelectAuthListItemHeadingClass")}>{msg(authenticationSelection.displayName)}</div>
<div className={getClassName("kcSelectAuthListItemDescriptionClass")}>{msg(authenticationSelection.helpText)}</div>
<div className={kcClsx("kcSelectAuthListItemBodyClass")}>
<div className={kcClsx("kcSelectAuthListItemHeadingClass")}>{msg(authenticationSelection.displayName)}</div>
<div className={kcClsx("kcSelectAuthListItemDescriptionClass")}>{msg(authenticationSelection.helpText)}</div>
</div>
<div className={getClassName("kcSelectAuthListItemFillClass")} />
<div className={getClassName("kcSelectAuthListItemArrowClass")}>
<i className={getClassName("kcSelectAuthListItemArrowIconClass")} />
<div className={kcClsx("kcSelectAuthListItemFillClass")} />
<div className={kcClsx("kcSelectAuthListItemArrowClass")}>
<i className={kcClsx("kcSelectAuthListItemArrowIconClass")} />
</div>
</button>
))}

View File

@ -1,6 +1,5 @@
import { clsx } from "keycloakify/tools/clsx";
import { Markdown } from "keycloakify/tools/Markdown";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import { useTermsMarkdown } from "keycloakify/login/lib/useDownloadTerms";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
@ -9,7 +8,7 @@ import { useI18n } from "../i18n";
export default function Terms(props: PageProps<Extract<KcContext, { pageId: "terms.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -25,26 +24,20 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
}
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("termsTitle")}>
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} displayMessage={false} headerNode={msg("termsTitle")}>
<div id="kc-terms-text" lang={termsLanguageTag !== locale?.currentLanguageTag ? termsLanguageTag : undefined}>
<Markdown>{termsMarkdown}</Markdown>
</div>
<form className="form-actions" action={url.loginAction} method="POST">
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonClass"),
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonClass", "kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
name="accept"
id="kc-accept"
type="submit"
value={msgStr("doAccept")}
/>
<input
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
name="cancel"
id="kc-decline"
type="submit"

View File

@ -1,7 +1,6 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx";
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
@ -14,7 +13,7 @@ type UpdateEmailProps = PageProps<Extract<KcContext, { pageId: "update-email.ftl
export default function UpdateEmail(props: UpdateEmailProps) {
const { kcContext, doUseDefaultCss, Template, classes, UserProfileFormFields } = props;
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
@ -28,47 +27,38 @@ export default function UpdateEmail(props: UpdateEmailProps) {
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={messagesPerField.exists("global")}
displayRequiredFields
headerNode={msg("updateEmailTitle")}
>
<form id="kc-update-email-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<UserProfileFormFields
{...{
kcContext,
getClassName,
messagesPerField
}}
onIsFormSubmittableValueChange={setIsFormSubmittable}
/>
<form id="kc-update-email-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<UserProfileFormFields kcContext={kcContext} kcClsx={kcClsx} onIsFormSubmittableValueChange={setIsFormSubmittable} />
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")} />
<div className={kcClsx("kcFormGroupClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")} />
</div>
<LogoutOtherSessions {...{ getClassName, i18n }} />
<LogoutOtherSessions kcClsx={kcClsx} i18n={i18n} />
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
disabled={!isFormSubmittable}
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
isAppInitiatedAction && getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
className={kcClsx(
"kcButtonClass",
"kcButtonPrimaryClass",
isAppInitiatedAction && "kcButtonBlockClass",
"kcButtonLargeClass"
)}
type="submit"
value={msgStr("doSubmit")}
/>
{isAppInitiatedAction && (
<button
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
type="submit"
name="cancel-aia"
value="true"
@ -83,14 +73,14 @@ export default function UpdateEmail(props: UpdateEmailProps) {
);
}
function LogoutOtherSessions(props: { getClassName: ReturnType<typeof useGetClassName>["getClassName"]; i18n: I18n }) {
const { getClassName, i18n } = props;
function LogoutOtherSessions(props: { kcClsx: KcClsx; i18n: I18n }) {
const { kcClsx, i18n } = props;
const { msg } = i18n;
return (
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
<div className="checkbox">
<label>
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" defaultChecked={true} />

View File

@ -2,7 +2,7 @@ import { useEffect, Fragment } from "react";
import { assert } from "tsafe/assert";
import { clsx } from "keycloakify/tools/clsx";
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -10,7 +10,7 @@ import { useI18n } from "../i18n";
export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext, { pageId: "webauthn-authenticate.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
const {
url,
@ -136,7 +136,9 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
displayMessage={!messagesPerField.existsError("username")}
displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
infoNode={
@ -151,7 +153,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
}
headerNode={msg("webauthn-login-title")}
>
<div id="kc-form-webauthn" className={getClassName("kcFormClass")}>
<div id="kc-form-webauthn" className={kcClsx("kcFormClass")}>
<form id="webauth" action={url.loginAction} method="post">
<input type="hidden" id="clientDataJSON" name="clientDataJSON" />
<input type="hidden" id="authenticatorData" name="authenticatorData" />
@ -160,10 +162,10 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
<input type="hidden" id="userHandle" name="userHandle" />
<input type="hidden" id="error" name="error" />
</form>
<div className={clsx(getClassName("kcFormGroupClass"), "no-bottom-margin")}>
<div className={clsx(kcClsx("kcFormGroupClass"), "no-bottom-margin")}>
{authenticators && (
<>
<form id="authn_select" className={getClassName("kcFormClass")}>
<form id="authn_select" className={kcClsx("kcFormClass")}>
{authenticators.authenticators.map(authenticator => (
<input type="hidden" name="authn_use_chk" value={authenticator.credentialId} />
))}
@ -172,36 +174,33 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
{shouldDisplayAuthenticators && (
<>
{authenticators.authenticators.length > 1 && (
<p className={getClassName("kcSelectAuthListItemTitle")}>{msg("webauthn-available-authenticators")}</p>
<p className={kcClsx("kcSelectAuthListItemTitle")}>{msg("webauthn-available-authenticators")}</p>
)}
<div className={getClassName("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsClass")}>
{authenticators.authenticators.map((authenticator, i) => (
<div key={i} id="kc-webauthn-authenticator" className={getClassName("kcSelectAuthListItemClass")}>
<div className={getClassName("kcSelectAuthListItemIconClass")}>
<div key={i} id="kc-webauthn-authenticator" className={kcClsx("kcSelectAuthListItemClass")}>
<div className={kcClsx("kcSelectAuthListItemIconClass")}>
<i
className={clsx(
(() => {
const className = getClassName(authenticator.transports.iconClass as any);
const className = kcClsx(authenticator.transports.iconClass as any);
if (className === authenticator.transports.iconClass) {
return getClassName("kcWebAuthnDefaultIcon");
return kcClsx("kcWebAuthnDefaultIcon");
}
return className;
})(),
getClassName("kcSelectAuthListItemIconPropertyClass")
kcClsx("kcSelectAuthListItemIconPropertyClass")
)}
/>
</div>
<div className={getClassName("kcSelectAuthListItemArrowIconClass")}>
<div
id="kc-webauthn-authenticator-label"
className={getClassName("kcSelectAuthListItemHeadingClass")}
>
<div className={kcClsx("kcSelectAuthListItemArrowIconClass")}>
<div id="kc-webauthn-authenticator-label" className={kcClsx("kcSelectAuthListItemHeadingClass")}>
{advancedMsg(authenticator.label)}
</div>
{authenticator.transports.displayNameProperties?.length && (
<div
id="kc-webauthn-authenticator-transport"
className={getClassName("kcSelectAuthListItemDescriptionClass")}
className={kcClsx("kcSelectAuthListItemDescriptionClass")}
>
{authenticator.transports.displayNameProperties
.map((nameProperty, i, arr) => ({
@ -216,11 +215,11 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
))}
</div>
)}
<div className={getClassName("kcSelectAuthListItemDescriptionClass")}>
<div className={kcClsx("kcSelectAuthListItemDescriptionClass")}>
<span id="kc-webauthn-authenticator-created-label">{msg("webauthn-createdAt-label")}</span>
<span id="kc-webauthn-authenticator-created">{authenticator.createdAt}</span>
</div>
<div className={getClassName("kcSelectAuthListItemFillClass")} />
<div className={kcClsx("kcSelectAuthListItemFillClass")} />
</div>
</div>
))}
@ -230,7 +229,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
</>
)}
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
<div id="kc-form-buttons" className={kcClsx("kcFormButtonsClass")}>
<input
id="authenticateWebAuthnButton"
type="button"
@ -241,12 +240,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
}}
autoFocus
value={msgStr("webauthn-doAuthenticate")}
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
/>
</div>
</div>

View File

@ -1,5 +1,4 @@
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import { useI18n } from "../i18n";
@ -11,14 +10,14 @@ export default function WebauthnError(props: PageProps<Extract<KcContext, { page
const { msg, msgStr } = useI18n({ kcContext });
const { getClassName } = useGetClassName({
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
return (
<Template {...{ kcContext, doUseDefaultCss, classes }} displayMessage headerNode={msg("webauthn-error-title")}>
<form id="kc-error-credential-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} displayMessage headerNode={msg("webauthn-error-title")}>
<form id="kc-error-credential-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<input type="hidden" id="executionValue" name="authenticationExecution" />
<input type="hidden" id="isSetRetry" name="isSetRetry" />
</form>
@ -33,26 +32,16 @@ export default function WebauthnError(props: PageProps<Extract<KcContext, { page
document.getElementById("kc-error-credential-form").submit();
}}
type="button"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
name="try-again"
id="kc-try-again"
value={msgStr("doTryAgain")}
/>
{isAppInitiatedAction && (
<form action={url.loginAction} className={getClassName("kcFormClass")} id="kc-webauthn-settings-form" method="post">
<form action={url.loginAction} className={kcClsx("kcFormClass")} id="kc-webauthn-settings-form" method="post">
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonBlockClass", "kcButtonLargeClass")}
id="cancelWebAuthnAIA"
name="cancel-aia"
value="true"

View File

@ -1,7 +1,6 @@
import { useEffect } from "react";
import { assert } from "tsafe/assert";
import { clsx } from "keycloakify/tools/clsx";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
@ -10,7 +9,7 @@ import { useI18n, type I18n } from "../i18n";
export default function WebauthnRegister(props: PageProps<Extract<KcContext, { pageId: "webauthn-register.ftl" }>>) {
const { kcContext, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
const {
url,
@ -207,33 +206,30 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
return (
<Template
{...{ kcContext, doUseDefaultCss, classes }}
kcContext={kcContext}
doUseDefaultCss={doUseDefaultCss}
classes={classes}
headerNode={
<>
<span className={getClassName("kcWebAuthnKeyIcon")} />
<span className={kcClsx("kcWebAuthnKeyIcon")} />
{msg("webauthn-registration-title")}
</>
}
>
<form id="register" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<div className={getClassName("kcFormGroupClass")}>
<form id="register" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
<div className={kcClsx("kcFormGroupClass")}>
<input type="hidden" id="clientDataJSON" name="clientDataJSON" />
<input type="hidden" id="attestationObject" name="attestationObject" />
<input type="hidden" id="publicKeyCredentialId" name="publicKeyCredentialId" />
<input type="hidden" id="authenticatorLabel" name="authenticatorLabel" />
<input type="hidden" id="transports" name="transports" />
<input type="hidden" id="error" name="error" />
<LogoutOtherSessions {...{ i18n, getClassName }} />
<LogoutOtherSessions kcClsx={kcClsx} i18n={i18n} />
</div>
</form>
<input
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonBlockClass", "kcButtonLargeClass")}
id="registerWebAuthn"
value={msgStr("doRegisterSecurityKey")}
onClick={() => {
@ -244,15 +240,10 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
/>
{!isSetRetry && isAppInitiatedAction && (
<form action={url.loginAction} className={getClassName("kcFormClass")} id="kc-webauthn-settings-form" method="post">
<form action={url.loginAction} className={kcClsx("kcFormClass")} id="kc-webauthn-settings-form" method="post">
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonBlockClass", "kcButtonLargeClass")}
id="cancelWebAuthnAIA"
name="cancel-aia"
value="true"
@ -265,14 +256,14 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
);
}
function LogoutOtherSessions(props: { i18n: I18n; getClassName: ReturnType<typeof useGetClassName>["getClassName"] }) {
const { getClassName, i18n } = props;
function LogoutOtherSessions(props: { kcClsx: KcClsx; i18n: I18n }) {
const { kcClsx, i18n } = props;
const { msg } = i18n;
return (
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}>
<div id="kc-form-options" className={kcClsx("kcFormOptionsClass")}>
<div className={kcClsx("kcFormOptionsWrapperClass")}>
<div className="checkbox">
<label>
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" defaultChecked={true} />

View File

@ -1,50 +1,8 @@
import { assert } from "tsafe/assert";
import { typeGuard } from "tsafe/typeGuard";
import { id } from "tsafe";
import { clsx_withTransform, type CxArg as CxArg_generic } from "./clsx_withTransform";
export type CxArg =
| undefined
| null
| string
| boolean
| Partial<Record<string, boolean | null | undefined>>
| readonly CxArg[];
export type CxArg = CxArg_generic<string>;
export const clsx = (...args: CxArg[]): string => {
const len = args.length;
let i = 0;
let cls = "";
for (; i < len; i++) {
const arg = args[i];
if (arg == null) continue;
let toAdd;
switch (typeof arg) {
case "boolean":
break;
case "object": {
if (Array.isArray(arg)) {
toAdd = clsx(...arg);
} else {
assert(!typeGuard<{ length: number }>(arg, false));
toAdd = "";
for (const k in arg) {
if (arg[k as string] && k) {
toAdd && (toAdd += " ");
toAdd += k;
export function clsx(...args: CxArg[]): string {
return clsx_withTransform({ args, transform: id });
}
}
}
break;
}
default: {
toAdd = arg;
}
}
if (toAdd) {
cls && (cls += " ");
cls += toAdd;
}
}
return cls;
};

View File

@ -0,0 +1,55 @@
import { assert } from "tsafe/assert";
import { typeGuard } from "tsafe/typeGuard";
export type CxArg<ClassName extends string = string> =
| undefined
| null
| ClassName
| boolean
| Partial<Record<ClassName, boolean | null | undefined>>
| readonly CxArg<ClassName>[];
export function clsx_withTransform(params: {
args: CxArg[];
transform: (arg: string) => string;
}): string {
const { args, transform } = params;
const len = args.length;
let i = 0;
let cls = "";
for (; i < len; i++) {
const arg = args[i];
if (arg == null) continue;
let toAdd;
switch (typeof arg) {
case "boolean":
break;
case "object": {
if (Array.isArray(arg)) {
toAdd = clsx_withTransform({ args: arg, transform });
} else {
assert(!typeGuard<{ length: number }>(arg, false));
toAdd = "";
for (const k in arg) {
if (arg[k] && k) {
toAdd && (toAdd += " ");
toAdd += transform(k);
}
}
}
break;
}
default: {
toAdd = transform(arg);
}
}
if (toAdd) {
cls && (cls += " ");
cls += toAdd;
}
}
return cls;
}

View File

@ -1,17 +1,10 @@
import React from "react";
import Fallback from "../../dist/account/Fallback";
import type { KcContext } from "./KcContext";
import { useI18n } from "./i18n";
import Template from "../../dist/account/Template";
export default function KcApp(props: { kcContext: KcContext }) {
const { kcContext } = props;
const i18n = useI18n({ kcContext });
if (i18n === null) {
return null;
}
return <Fallback {...{ kcContext, i18n }} Template={Template} doUseDefaultCss={true} />;
return <Fallback kcContext={kcContext} Template={Template} doUseDefaultCss={true} />;
}

View File

@ -1,10 +1,11 @@
import type { ExtendKcContext } from "../../dist/account";
import type { KcEnvName, ThemeName } from "../kc.gen";
export type KcContextExtraProperties = {};
export type KcContextExtension = {
themeName: ThemeName;
properties: Record<KcEnvName, string> & {};
};
export type KcContextExtraPropertiesPerPage = {};
export type KcContextExtensionPerPage = {};
export type KcContext = ExtendKcContext<
KcContextExtraProperties,
KcContextExtraPropertiesPerPage
>;
export type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;

View File

@ -1,16 +1,24 @@
import React from "react";
import type { KcContext } from "./KcContext";
import KcApp from "./KcApp";
import type { DeepPartial } from "../../dist/tools/DeepPartial";
import type { KcContext } from "./KcContext";
import { createGetKcContextMock } from "../../dist/account/KcContext";
import type { KcContextExtraProperties, KcContextExtraPropertiesPerPage } from "./KcContext";
import type { KcContextExtension, KcContextExtensionPerPage } from "./KcContext";
import KcApp from "./KcApp";
import { themeNames, kcEnvDefaults } from "../kc.gen";
const kcContextExtraProperties: KcContextExtraProperties = {};
const kcContextExtraPropertiesPerPage: KcContextExtraPropertiesPerPage = {};
const kcContextExtension: KcContextExtension = {
themeName: themeNames[0],
properties: {
...kcEnvDefaults
}
};
const kcContextExtensionPerPage: KcContextExtensionPerPage = {};
export const { getKcContextMock } = createGetKcContextMock({
kcContextExtraProperties,
kcContextExtraPropertiesPerPage
kcContextExtension,
kcContextExtensionPerPage,
overrides: {},
overridesPerPage: {}
});
export function createPageStory<PageId extends KcContext["pageId"]>(params: { pageId: PageId }) {

7
stories/kc.gen.ts Normal file
View File

@ -0,0 +1,7 @@
export const themeNames = ["keycloakify"] as const;
export type ThemeName = (typeof themeNames)[number];
export type KcEnvName = never;
export const kcEnvDefaults: Record<KcEnvName, string> = {};

View File

@ -1,7 +1,6 @@
import React from "react";
import Fallback from "../../dist/login/Fallback";
import type { KcContext } from "./KcContext";
import { useI18n } from "./i18n";
import { useDownloadTerms } from "../../dist/login/lib/useDownloadTerms";
import Template from "../../dist/login/Template";
import UserProfileFormFields from "../../dist/login/UserProfileFormFields";
@ -9,8 +8,6 @@ import UserProfileFormFields from "../../dist/login/UserProfileFormFields";
export default function KcApp(props: { kcContext: KcContext }) {
const { kcContext } = props;
const i18n = useI18n({ kcContext });
useDownloadTerms({
kcContext,
downloadTermsMarkdown: async ({ currentLanguageTag }) => {
@ -36,19 +33,5 @@ export default function KcApp(props: { kcContext: KcContext }) {
}
});
if (i18n === null) {
return null;
}
return (
<Fallback
{...{
kcContext,
i18n,
Template,
UserProfileFormFields
}}
doUseDefaultCss={true}
/>
);
return <Fallback kcContext={kcContext} Template={Template} UserProfileFormFields={UserProfileFormFields} doUseDefaultCss={true} />;
}

View File

@ -1,10 +1,11 @@
import type { ExtendKcContext } from "../../dist/login";
import type { KcEnvName, ThemeName } from "../kc.gen";
export type KcContextExtraProperties = {};
export type KcContextExtension = {
themeName: ThemeName;
properties: Record<KcEnvName, string> & {};
};
export type KcContextExtraPropertiesPerPage = {};
export type KcContextExtensionPerPage = {};
export type KcContext = ExtendKcContext<
KcContextExtraProperties,
KcContextExtraPropertiesPerPage
>;
export type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;

View File

@ -1,16 +1,24 @@
import React from "react";
import type { KcContext } from "./KcContext";
import KcApp from "./KcApp";
import type { DeepPartial } from "../../dist/tools/DeepPartial";
import type { KcContext } from "./KcContext";
import { createGetKcContextMock } from "../../dist/login/KcContext";
import type { KcContextExtraProperties, KcContextExtraPropertiesPerPage } from "./KcContext";
import type { KcContextExtension, KcContextExtensionPerPage } from "./KcContext";
import KcApp from "./KcApp";
import { themeNames, kcEnvDefaults } from "../kc.gen";
const kcContextExtraProperties: KcContextExtraProperties = {};
const kcContextExtraPropertiesPerPage: KcContextExtraPropertiesPerPage = {};
const kcContextExtension: KcContextExtension = {
themeName: themeNames[0],
properties: {
...kcEnvDefaults
}
};
const kcContextExtensionPerPage: KcContextExtensionPerPage = {};
const { getKcContextMock } = createGetKcContextMock({
kcContextExtraProperties,
kcContextExtraPropertiesPerPage
export const { getKcContextMock } = createGetKcContextMock({
kcContextExtension,
kcContextExtensionPerPage,
overrides: {},
overridesPerPage: {}
});
export function createPageStory<PageId extends KcContext["pageId"]>(params: { pageId: PageId }) {

View File

@ -5,13 +5,13 @@ import { assert, type Equals } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
{
type KcContextExtraProperties = {
type KcContextExtension = {
properties: {
myCustomProperty: string | undefined;
};
};
type KcContextExtraPropertiesPerPage = {
type KcContextExtensionPerPage = {
"login.ftl": {
foo: string;
};
@ -20,10 +20,7 @@ import { Reflect } from "tsafe/Reflect";
};
};
type KcContext = ExtendKcContext<
KcContextExtraProperties,
KcContextExtraPropertiesPerPage
>;
type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;
{
type Got = Extract<KcContext, { pageId: "login.ftl" }>;
@ -47,7 +44,7 @@ import { Reflect } from "tsafe/Reflect";
type Got = Extract<KcContext, { pageId: "my-custom-page.ftl" }>;
type Expected = KcContextBase.Common &
KcContextExtraProperties & { pageId: "my-custom-page.ftl" } & {
KcContextExtension & { pageId: "my-custom-page.ftl" } & {
properties: { myCustomProperty: string | undefined };
} & { bar: number };
@ -56,8 +53,8 @@ import { Reflect } from "tsafe/Reflect";
}
const { getKcContextMock } = createGetKcContextMock({
kcContextExtraProperties: Reflect<KcContextExtraProperties>(),
kcContextExtraPropertiesPerPage: Reflect<KcContextExtraPropertiesPerPage>()
kcContextExtension: Reflect<KcContextExtension>(),
kcContextExtensionPerPage: Reflect<KcContextExtensionPerPage>()
});
{
@ -104,8 +101,8 @@ import { Reflect } from "tsafe/Reflect";
});
createGetKcContextMock({
kcContextExtraProperties: Reflect<KcContextExtraProperties>(),
kcContextExtraPropertiesPerPage: Reflect<KcContextExtraPropertiesPerPage>(),
kcContextExtension: Reflect<KcContextExtension>(),
kcContextExtensionPerPage: Reflect<KcContextExtensionPerPage>(),
overrides: {
locale: {
currentLanguageTag: "fr"
@ -137,8 +134,8 @@ import { Reflect } from "tsafe/Reflect";
});
createGetKcContextMock({
kcContextExtraProperties: Reflect<KcContextExtraProperties>(),
kcContextExtraPropertiesPerPage: Reflect<KcContextExtraPropertiesPerPage>(),
kcContextExtension: Reflect<KcContextExtension>(),
kcContextExtensionPerPage: Reflect<KcContextExtensionPerPage>(),
overridesPerPage: {
"register.ftl": {
// @ts-expect-error
@ -149,14 +146,11 @@ import { Reflect } from "tsafe/Reflect";
}
{
type KcContextExtraProperties = {};
type KcContextExtension = {};
type KcContextExtraPropertiesPerPage = {};
type KcContextExtensionPerPage = {};
type KcContext = ExtendKcContext<
KcContextExtraProperties,
KcContextExtraPropertiesPerPage
>;
type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;
{
type Got = Extract<KcContext, { pageId: "login.ftl" }>;
@ -173,8 +167,8 @@ import { Reflect } from "tsafe/Reflect";
}
const { getKcContextMock } = createGetKcContextMock({
kcContextExtraProperties: Reflect<KcContextExtraProperties>(),
kcContextExtraPropertiesPerPage: Reflect<KcContextExtraPropertiesPerPage>()
kcContextExtension: Reflect<KcContextExtension>(),
kcContextExtensionPerPage: Reflect<KcContextExtensionPerPage>()
});
{

View File

@ -9,13 +9,13 @@ import { structuredCloneButFunctions } from "keycloakify/tools/structuredCloneBu
import { expect, it, describe } from "vitest";
describe("createGetKcContextMock", () => {
type KcContextExtraProperties = {
type KcContextExtension = {
properties: {
MY_ENV_VAR?: string;
};
};
type KcContextExtraPropertiesPerPage = {
type KcContextExtensionPerPage = {
"register.ftl": {
authorizedMailDomains: string[];
};
@ -25,12 +25,12 @@ describe("createGetKcContextMock", () => {
};
const { getKcContextMock } = createGetKcContextMock({
kcContextExtraProperties: id<KcContextExtraProperties>({
kcContextExtension: id<KcContextExtension>({
properties: {
MY_ENV_VAR: "my env var value"
}
}),
kcContextExtraPropertiesPerPage: id<KcContextExtraPropertiesPerPage>({
kcContextExtensionPerPage: id<KcContextExtensionPerPage>({
"register.ftl": {
authorizedMailDomains: ["gmail.com", "hotmail.com"]
},