This commit is contained in:
Joseph Garrone 2024-07-04 19:53:57 +02:00
parent 59f8814660
commit 7c257d97a7
16 changed files with 170 additions and 93 deletions

View File

@ -145,7 +145,12 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
<div className={clsx("alert", `alert-${message.type}`)}> <div className={clsx("alert", `alert-${message.type}`)}>
{message.type === "success" && <span className="pficon pficon-ok"></span>} {message.type === "success" && <span className="pficon pficon-ok"></span>}
{message.type === "error" && <span className="pficon pficon-error-circle-o"></span>} {message.type === "error" && <span className="pficon pficon-error-circle-o"></span>}
<span className="kc-feedback-text">{message.summary}</span> <span
className="kc-feedback-text"
dangerouslySetInnerHTML={{
__html: message.summary
}}
/>
</div> </div>
)} )}

View File

@ -154,9 +154,14 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
/> />
{messagesPerField.existsError("totp") && ( {messagesPerField.existsError("totp") && (
<span id="input-error-otp-code" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("totp")} id="input-error-otp-code"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("totp")
}}
/>
)} )}
</div> </div>
<input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} /> <input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
@ -180,9 +185,14 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
aria-invalid={messagesPerField.existsError("userLabel")} aria-invalid={messagesPerField.existsError("userLabel")}
/> />
{messagesPerField.existsError("userLabel") && ( {messagesPerField.existsError("userLabel") && (
<span id="input-error-otp-label" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("userLabel")} id="input-error-otp-label"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("userLabel")
}}
/>
)} )}
</div> </div>
</div> </div>

View File

@ -15,7 +15,6 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
displayMessage = true, displayMessage = true,
displayRequiredFields = false, displayRequiredFields = false,
headerNode, headerNode,
showUsernameNode = null,
socialProvidersNode = null, socialProvidersNode = null,
infoNode = null, infoNode = null,
documentTitle, documentTitle,
@ -164,45 +163,10 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
</div> </div>
</div> </div>
)} )}
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? ( {(() => {
displayRequiredFields ? ( const node = !(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
<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">
<h1 id="kc-page-title">{headerNode}</h1>
</div>
</div>
) : (
<h1 id="kc-page-title">{headerNode}</h1> <h1 id="kc-page-title">{headerNode}</h1>
) ) : (
) : displayRequiredFields ? (
<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={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={kcClsx("kcResetFlowIcon")}></i>
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
</div>
</a>
</div>
</div>
</div>
) : (
<>
{showUsernameNode}
<div id="kc-username" className={kcClsx("kcFormGroupClass")}> <div id="kc-username" className={kcClsx("kcFormGroupClass")}>
<label id="kc-attempted-username">{auth.attemptedUsername}</label> <label id="kc-attempted-username">{auth.attemptedUsername}</label>
<a id="reset-login" href={url.loginRestartFlowUrl} aria-label={msgStr("restartLoginTooltip")}> <a id="reset-login" href={url.loginRestartFlowUrl} aria-label={msgStr("restartLoginTooltip")}>
@ -212,8 +176,24 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
</div> </div>
</a> </a>
</div> </div>
</> );
)}
if (displayRequiredFields) {
return (
<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">{node}</div>
</div>
);
}
return node;
})()}
</header> </header>
<div id="kc-content"> <div id="kc-content">
<div id="kc-content-wrapper"> <div id="kc-content-wrapper">

View File

@ -12,7 +12,6 @@ export type TemplateProps<KcContext, I18n> = {
displayRequiredFields?: boolean; displayRequiredFields?: boolean;
showAnotherWayIfPresent?: boolean; showAnotherWayIfPresent?: boolean;
headerNode: ReactNode; headerNode: ReactNode;
showUsernameNode?: ReactNode;
socialProvidersNode?: ReactNode; socialProvidersNode?: ReactNode;
infoNode?: ReactNode; infoNode?: ReactNode;
documentTitle?: string; documentTitle?: string;

View File

@ -188,7 +188,7 @@ function FieldErrors(props: { attribute: Attribute; displayableErrors: FormField
.filter(error => error.fieldIndex === fieldIndex) .filter(error => error.fieldIndex === fieldIndex)
.map(({ errorMessage }, i, arr) => ( .map(({ errorMessage }, i, arr) => (
<Fragment key={i}> <Fragment key={i}>
<span key={i}>{errorMessage}</span> {errorMessage}
{arr.length - 1 !== i && <br />} {arr.length - 1 !== i && <br />}
</Fragment> </Fragment>
))} ))}

View File

@ -612,7 +612,14 @@ function useGetErrors(params: { kcContext: KcContextLike_useGetErrors; i18n: I18
return [ return [
{ {
errorMessageStr, errorMessageStr,
errorMessage: <span key={0}>{errorMessageStr}</span>, errorMessage: (
<span
key={0}
dangerouslySetInnerHTML={{
__html: errorMessageStr
}}
/>
),
fieldIndex: undefined, fieldIndex: undefined,
source: { source: {
type: "server" type: "server"

View File

@ -19,7 +19,7 @@ export default function Error(props: PageProps<Extract<KcContext, { pageId: "err
headerNode={msg("errorTitle")} headerNode={msg("errorTitle")}
> >
<div id="kc-error-message"> <div id="kc-error-message">
<p className="instruction">{message.summary}</p> <p className="instruction" dangerouslySetInnerHTML={{ __html: message.summary }} />
{!skipLink && client !== undefined && client.baseUrl !== undefined && ( {!skipLink && client !== undefined && client.baseUrl !== undefined && (
<p> <p>
<a id="backToApplication" href={client.baseUrl}> <a id="backToApplication" href={client.baseUrl}>

View File

@ -16,13 +16,33 @@ export default function Info(props: PageProps<Extract<KcContext, { pageId: "info
doUseDefaultCss={doUseDefaultCss} doUseDefaultCss={doUseDefaultCss}
classes={classes} classes={classes}
displayMessage={false} displayMessage={false}
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>} headerNode={
<span
dangerouslySetInnerHTML={{
__html: messageHeader ?? message.summary
}}
/>
}
> >
<div id="kc-info-message"> <div id="kc-info-message">
<p className="instruction"> <p
{message.summary} className="instruction"
{requiredActions && <b>{requiredActions.map(requiredAction => msgStr(`requiredAction.${requiredAction}` as const)).join(",")}</b>} dangerouslySetInnerHTML={{
</p> __html: (() => {
let html = message.summary;
if (requiredActions) {
html += "<b>";
html += requiredActions.map(requiredAction => msgStr(`requiredAction.${requiredAction}` as const)).join(",");
html += "</b>";
}
return html;
})()
}}
/>
{(() => { {(() => {
if (skipLink) { if (skipLink) {
return null; return null;

View File

@ -60,9 +60,10 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
href={p.loginUrl} href={p.loginUrl}
> >
{p.iconClasses && <i className={clsx(kcClsx("kcCommonLogoIdP"), p.iconClasses)} aria-hidden="true"></i>} {p.iconClasses && <i className={clsx(kcClsx("kcCommonLogoIdP"), p.iconClasses)} aria-hidden="true"></i>}
<span className={clsx(kcClsx("kcFormSocialAccountNameClass"), p.iconClasses && "kc-social-icon-text")}> <span
{p.displayName} className={clsx(kcClsx("kcFormSocialAccountNameClass"), p.iconClasses && "kc-social-icon-text")}
</span> dangerouslySetInnerHTML={{ __html: p.displayName }}
></span>
</a> </a>
</li> </li>
))} ))}
@ -105,9 +106,14 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
aria-invalid={messagesPerField.existsError("username", "password")} aria-invalid={messagesPerField.existsError("username", "password")}
/> />
{messagesPerField.existsError("username", "password") && ( {messagesPerField.existsError("username", "password") && (
<span id="input-error" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.getFirstError("username", "password")} id="input-error"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.getFirstError("username", "password")
}}
/>
)} )}
</div> </div>
)} )}
@ -128,9 +134,14 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
/> />
</PasswordWrapper> </PasswordWrapper>
{usernameHidden && messagesPerField.existsError("username", "password") && ( {usernameHidden && messagesPerField.existsError("username", "password") && (
<span id="input-error" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.getFirstError("username", "password")} id="input-error"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.getFirstError("username", "password")
}}
/>
)} )}
</div> </div>

View File

@ -112,9 +112,14 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
/> />
{messagesPerField.existsError("totp") && ( {messagesPerField.existsError("totp") && (
<span id="input-error-otp-code" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("totp")} id="input-error-otp-code"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("totp")
}}
/>
)} )}
</div> </div>
<input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} /> <input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
@ -138,9 +143,14 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
aria-invalid={messagesPerField.existsError("userLabel")} aria-invalid={messagesPerField.existsError("userLabel")}
/> />
{messagesPerField.existsError("userLabel") && ( {messagesPerField.existsError("userLabel") && (
<span id="input-error-otp-label" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("userLabel")} id="input-error-otp-label"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("userLabel")
}}
/>
)} )}
</div> </div>
</div> </div>

View File

@ -70,9 +70,14 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
aria-invalid={messagesPerField.existsError("totp")} aria-invalid={messagesPerField.existsError("totp")}
/> />
{messagesPerField.existsError("totp") && ( {messagesPerField.existsError("totp") && (
<span id="input-error-otp-code" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("totp")} id="input-error-otp-code"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("totp")
}}
/>
)} )}
</div> </div>
</div> </div>

View File

@ -60,9 +60,14 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
</PasswordWrapper> </PasswordWrapper>
{messagesPerField.existsError("password") && ( {messagesPerField.existsError("password") && (
<span id="input-error-password" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("password")} id="input-error-password"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("password")
}}
/>
)} )}
</div> </div>
<div className={kcClsx("kcFormGroupClass", "kcFormSettingClass")}> <div className={kcClsx("kcFormGroupClass", "kcFormSettingClass")}>

View File

@ -43,9 +43,14 @@ export default function LoginRecoveryAuthnCodeInput(props: PageProps<Extract<KcC
autoFocus autoFocus
/> />
{messagesPerField.existsError("recoveryCodeInput") && ( {messagesPerField.existsError("recoveryCodeInput") && (
<span id="input-error" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("recoveryCodeInput")} id="input-error"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("recoveryCodeInput")
}}
/>
)} )}
</div> </div>
</div> </div>

View File

@ -48,9 +48,14 @@ export default function LoginResetPassword(props: PageProps<Extract<KcContext, {
aria-invalid={messagesPerField.existsError("username")} aria-invalid={messagesPerField.existsError("username")}
/> />
{messagesPerField.existsError("username") && ( {messagesPerField.existsError("username") && (
<span id="input-error-username" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("username")} id="input-error-username"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("username")
}}
/>
)} )}
</div> </div>
</div> </div>

View File

@ -46,9 +46,14 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
</PasswordWrapper> </PasswordWrapper>
{messagesPerField.existsError("password") && ( {messagesPerField.existsError("password") && (
<span id="input-error-password" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("password")} id="input-error-password"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("password")
}}
/>
)} )}
</div> </div>
</div> </div>
@ -74,9 +79,14 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
</PasswordWrapper> </PasswordWrapper>
{messagesPerField.existsError("password-confirm") && ( {messagesPerField.existsError("password-confirm") && (
<span id="input-error-password-confirm" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("password-confirm")} id="input-error-password-confirm"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("password-confirm")
}}
/>
)} )}
</div> </div>

View File

@ -118,9 +118,14 @@ function TermsAcceptance(props: {
</div> </div>
{messagesPerField.existsError("termsAccepted") && ( {messagesPerField.existsError("termsAccepted") && (
<div className={kcClsx("kcLabelWrapperClass")}> <div className={kcClsx("kcLabelWrapperClass")}>
<span id="input-error-terms-accepted" className={kcClsx("kcInputErrorMessageClass")} aria-live="polite"> <span
{messagesPerField.get("termsAccepted")} id="input-error-terms-accepted"
</span> className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: messagesPerField.get("termsAccepted")
}}
/>
</div> </div>
)} )}
</div> </div>