Refactor terms
This commit is contained in:
parent
fdead071e7
commit
54b129630e
@ -5,7 +5,6 @@ import type { I18n } from "./i18n";
|
||||
import type { KcContext } from "./kcContext";
|
||||
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
|
||||
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
|
||||
import type { TermsAcceptanceProps } from "keycloakify/login/TermsAcceptance";
|
||||
|
||||
const Login = lazy(() => import("keycloakify/login/pages/Login"));
|
||||
const Register = lazy(() => import("keycloakify/login/pages/Register"));
|
||||
@ -35,7 +34,6 @@ const DeleteCredential = lazy(() => import("keycloakify/login/pages/DeleteCreden
|
||||
|
||||
type FallbackProps = PageProps<KcContext, I18n> & {
|
||||
UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>;
|
||||
TermsAcceptance: LazyOrNot<(props: TermsAcceptanceProps) => JSX.Element | null>;
|
||||
};
|
||||
|
||||
export default function Fallback(props: FallbackProps) {
|
||||
|
@ -1,81 +0,0 @@
|
||||
import type { ClassKey } from "keycloakify/login/TemplateProps";
|
||||
import { useRerenderOnStateChange } from "evt/hooks";
|
||||
import { Markdown } from "keycloakify/tools/Markdown";
|
||||
import { evtTermMarkdown } from "keycloakify/login/lib/useDownloadTerms";
|
||||
import type { KcContext } from "keycloakify/login/kcContext/KcContext";
|
||||
import type { I18n } from "./i18n";
|
||||
|
||||
export type TermsAcceptanceProps = {
|
||||
kcContext: KcContextLike;
|
||||
i18n: I18n;
|
||||
getClassName: (classKey: ClassKey) => string;
|
||||
};
|
||||
|
||||
type KcContextLike = {
|
||||
termsAcceptanceRequired?: boolean;
|
||||
messagesPerField: Pick<KcContext.Common["messagesPerField"], "existsError" | "get">;
|
||||
};
|
||||
|
||||
export function TermsAcceptance(props: TermsAcceptanceProps) {
|
||||
const {
|
||||
kcContext: { termsAcceptanceRequired = false }
|
||||
} = props;
|
||||
|
||||
if (!termsAcceptanceRequired) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <TermsAcceptanceEnabled {...props} />;
|
||||
}
|
||||
|
||||
export function TermsAcceptanceEnabled(props: TermsAcceptanceProps) {
|
||||
const {
|
||||
i18n,
|
||||
getClassName,
|
||||
kcContext: { messagesPerField }
|
||||
} = props;
|
||||
|
||||
const { msg } = i18n;
|
||||
|
||||
useRerenderOnStateChange(evtTermMarkdown);
|
||||
|
||||
const termMarkdown = evtTermMarkdown.state;
|
||||
|
||||
if (termMarkdown === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="form-group">
|
||||
<div className={getClassName("kcInputWrapperClass")}>
|
||||
{msg("termsTitle")}
|
||||
<div id="kc-registration-terms-text">
|
||||
<Markdown>{termMarkdown}</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<div className={getClassName("kcLabelWrapperClass")}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="termsAccepted"
|
||||
name="termsAccepted"
|
||||
className={getClassName("kcCheckboxInputClass")}
|
||||
aria-invalid={messagesPerField.existsError("termsAccepted")}
|
||||
/>
|
||||
<label htmlFor="termsAccepted" className={getClassName("kcLabelClass")}>
|
||||
{msg("acceptTerms")}
|
||||
</label>
|
||||
</div>
|
||||
{messagesPerField.existsError("termsAccepted") && (
|
||||
<div className={getClassName("kcLabelWrapperClass")}>
|
||||
<span id="input-error-terms-accepted" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
|
||||
{messagesPerField.get("termsAccepted")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -187,6 +187,7 @@ export declare namespace KcContext {
|
||||
* A Keycloak Java extension used as dependency in Keycloakify.
|
||||
*/
|
||||
passwordPolicies?: PasswordPolicies;
|
||||
termsAcceptanceRequired?: boolean;
|
||||
};
|
||||
|
||||
export type Info = Common & {
|
||||
|
@ -5,9 +5,10 @@ import { useConst } from "keycloakify/tools/useConst";
|
||||
import { useConstCallback } from "keycloakify/tools/useConstCallback";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Evt } from "evt";
|
||||
import { useRerenderOnStateChange } from "evt/hooks/useRerenderOnStateChange";
|
||||
import { KcContext } from "../kcContext";
|
||||
|
||||
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
|
||||
const evtTermsMarkdown = Evt.create<string | undefined>(undefined);
|
||||
|
||||
export type KcContextLike = {
|
||||
pageId: string;
|
||||
@ -41,8 +42,16 @@ export function useDownloadTerms(params: {
|
||||
useEffect(() => {
|
||||
if (kcContext.pageId === "terms.ftl" || kcContext.termsAcceptanceRequired) {
|
||||
downloadTermMarkdownMemoized(kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag).then(
|
||||
thermMarkdown => (evtTermMarkdown.state = thermMarkdown)
|
||||
thermMarkdown => (evtTermsMarkdown.state = thermMarkdown)
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function useTermsMarkdown() {
|
||||
useRerenderOnStateChange(evtTermsMarkdown);
|
||||
|
||||
const termsMarkdown = evtTermsMarkdown.state;
|
||||
|
||||
return { termsMarkdown };
|
||||
}
|
||||
|
@ -2,26 +2,26 @@ import { useState } from "react";
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
||||
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
|
||||
import { useTermsMarkdown } from "keycloakify/login/lib/useDownloadTerms";
|
||||
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
|
||||
import { Markdown } from "keycloakify/tools/Markdown";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
|
||||
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
|
||||
import type { TermsAcceptanceProps } from "../TermsAcceptance";
|
||||
|
||||
type RegisterProps = PageProps<Extract<KcContext, { pageId: "register.ftl" | "register-user-profile.ftl" }>, I18n> & {
|
||||
UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>;
|
||||
TermsAcceptance: LazyOrNot<(props: TermsAcceptanceProps) => JSX.Element | null>;
|
||||
};
|
||||
|
||||
export default function Register(props: RegisterProps) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes, UserProfileFormFields, TermsAcceptance } = props;
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes, UserProfileFormFields } = props;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
doUseDefaultCss,
|
||||
classes
|
||||
});
|
||||
|
||||
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey, termsAcceptanceRequired } = kcContext;
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
@ -39,7 +39,15 @@ export default function Register(props: RegisterProps) {
|
||||
}}
|
||||
onIsFormSubmittableValueChange={setIsFormSubmittable}
|
||||
/>
|
||||
<TermsAcceptance {...{ kcContext, i18n, getClassName }} />
|
||||
{termsAcceptanceRequired && (
|
||||
<TermsAcceptance
|
||||
{...{
|
||||
i18n,
|
||||
getClassName,
|
||||
messagesPerField
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{recaptchaRequired && (
|
||||
<div className="form-group">
|
||||
<div className={getClassName("kcInputWrapperClass")}>
|
||||
@ -73,3 +81,54 @@ export default function Register(props: RegisterProps) {
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
function TermsAcceptance(props: {
|
||||
i18n: I18n;
|
||||
getClassName: ReturnType<typeof useGetClassName>["getClassName"];
|
||||
messagesPerField: Pick<KcContext.Common["messagesPerField"], "existsError" | "get">;
|
||||
}) {
|
||||
const { i18n, getClassName, messagesPerField } = props;
|
||||
|
||||
const { msg } = i18n;
|
||||
|
||||
// NOTE: Refer to https://docs.keycloakify.dev/terms-and-conditions to load your terms and conditions.
|
||||
const { termsMarkdown } = useTermsMarkdown();
|
||||
|
||||
if (termsMarkdown === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="form-group">
|
||||
<div className={getClassName("kcInputWrapperClass")}>
|
||||
{msg("termsTitle")}
|
||||
<div id="kc-registration-terms-text">
|
||||
<Markdown>{termsMarkdown}</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<div className={getClassName("kcLabelWrapperClass")}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="termsAccepted"
|
||||
name="termsAccepted"
|
||||
className={getClassName("kcCheckboxInputClass")}
|
||||
aria-invalid={messagesPerField.existsError("termsAccepted")}
|
||||
/>
|
||||
<label htmlFor="termsAccepted" className={getClassName("kcLabelClass")}>
|
||||
{msg("acceptTerms")}
|
||||
</label>
|
||||
</div>
|
||||
{messagesPerField.existsError("termsAccepted") && (
|
||||
<div className={getClassName("kcLabelWrapperClass")}>
|
||||
<span id="input-error-terms-accepted" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
|
||||
{messagesPerField.get("termsAccepted")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import { useRerenderOnStateChange } from "evt/hooks";
|
||||
import { Markdown } from "keycloakify/tools/Markdown";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
||||
import { evtTermMarkdown } from "keycloakify/login/lib/useDownloadTerms";
|
||||
import { useTermsMarkdown } from "keycloakify/login/lib/useDownloadTerms";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
@ -17,20 +16,18 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
|
||||
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
useRerenderOnStateChange(evtTermMarkdown);
|
||||
|
||||
const { url } = kcContext;
|
||||
|
||||
const termMarkdown = evtTermMarkdown.state;
|
||||
const { termsMarkdown } = useTermsMarkdown();
|
||||
|
||||
if (termMarkdown === undefined) {
|
||||
if (termsMarkdown === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("termsTitle")}>
|
||||
<div id="kc-terms-text">
|
||||
<Markdown>{termMarkdown}</Markdown>
|
||||
<Markdown>{termsMarkdown}</Markdown>
|
||||
</div>
|
||||
<form className="form-actions" action={url.loginAction} method="POST">
|
||||
<input
|
||||
|
Loading…
x
Reference in New Issue
Block a user