Compare commits

...

15 Commits

10 changed files with 237 additions and 13 deletions

View File

@ -1,3 +1,22 @@
## **4.4.0** (2022-01-01)
- Merge pull request #73 from lazToum/main
(feature) added login-page-expired.ftl
- added login-page-expired.ftl
- Add update instruction for 4.3.0
## **4.3.0** (2021-12-27)
- Merge pull request #72 from praiz/main
feat(*): added login-update-password
- feat(*): added login-update-password
### **4.2.21** (2021-12-27)
- update dependencies
### **4.2.19** (2021-12-21)
- Merge pull request #70 from VBustamante/patch-1

View File

@ -5,11 +5,21 @@
<i>🔏 Create Keycloak themes using React 🔏</i>
<br>
<br>
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=develop">
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
<img src="https://img.shields.io/npm/dw/keycloakify">
<img src="https://img.shields.io/npm/l/keycloakify">
<img src="https://camo.githubusercontent.com/0f9fcc0ac1b8617ad4989364f60f78b2d6b32985ad6a508f215f14d8f897b8d3/68747470733a2f2f62616467656e2e6e65742f62616467652f547970655363726970742f7374726963742532302546302539462539322541412f626c7565">
<a href="https://github.com/garronej/keycloakify/actions">
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main">
</a>
<a href="https://bundlephobia.com/package/keycloakify">
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
</a>
<a href="https://www.npmjs.com/package/keycloakify">
<img src="https://img.shields.io/npm/dw/keycloakify">
</a>
<a href="https://github.com/garronej/keycloakify/blob/main/LICENSE">
<img src="https://img.shields.io/npm/l/keycloakify">
</a>
<a href="https://github.com/InseeFrLab/keycloakify/blob/729503fe31a155a823f46dd66ad4ff34ca274e0a/tsconfig.json#L14">
<img src="https://camo.githubusercontent.com/0f9fcc0ac1b8617ad4989364f60f78b2d6b32985ad6a508f215f14d8f897b8d3/68747470733a2f2f62616467656e2e6e65742f62616467652f547970655363726970742f7374726963742532302546302539462539322541412f626c7565">
</a>
<a href="https://github.com/thomasdarimont/awesome-keycloak">
<img src="https://awesome.re/mentioned-badge.svg"/>
</a>
@ -203,8 +213,8 @@ reactDom.render(
<img src="https://user-images.githubusercontent.com/6702424/114326299-6892fc00-9b34-11eb-8d75-85696e55458f.png">
</p>
Example of a customization using only CSS: [here](https://github.com/InseeFrLab/onyxia-ui/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/components/KcApp.tsx)
(the [index.tsx](https://github.com/InseeFrLab/onyxia-ui/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/index.tsx#L89-L94) )
Example of a customization using only CSS: [here](https://github.com/InseeFrLab/onyxia-web/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/components/KcApp.tsx)
(the [index.tsx](https://github.com/InseeFrLab/onyxia-web/blob/012639d62327a9a56be80c46e32c32c9497b82db/src/app/index.tsx#L89-L94) )
and the result you can expect:
<p align="center">
@ -219,7 +229,7 @@ If you want to go beyond only customizing the CSS you can re-implement some of t
pages or even add new ones.
If you want to go this way checkout the demo setup provided [here](https://github.com/garronej/keycloakify-demo-app/tree/look_and_feel).
If you prefer a real life example you can checkout [onyxia-web's source](https://github.com/InseeFrLab/onyxia-web/tree/main/src/app/components/KcApp).
If you prefer a real life example you can checkout [onyxia-web's source](https://github.com/InseeFrLab/onyxia-web/tree/main/src/ui/components/KcApp).
The web app is in production [here](https://datalab.sspcloud.fr).
Main takeaways are:
@ -425,7 +435,7 @@ keycloakInstance.init({
If you really want to go the extra miles and avoid having the white
flash of the blank html before the js bundle have been evaluated
[here is a snippet](https://github.com/InseeFrLab/onyxia-ui/blob/a77eb502870cfe6878edd0d956c646d28746d053/public/index.html#L5-L54) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
[here is a snippet](https://github.com/InseeFrLab/onyxia-web/blob/e1c1f309aaa3d5f860df39ba0b75cce89c88a9de/public/index.html#L117-L166) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
# Kickstart video
@ -465,6 +475,10 @@ and `kcRegisterContext["authorizedMailDomains"]` to validate on.
# Changelog highlights
> **New in v4.3.0**: Feature [`login-update-password.ftl`](https://user-images.githubusercontent.com/6702424/147517600-6191cf72-93dd-437b-a35c-47180142063e.png).
> Every time a page is added it's a breaking change for non CSS-only theme.
> Change [this](https://github.com/garronej/keycloakify-demo-app/blob/df664c13c77ce3c53ac7df0622d94d04e76d3f9f/src/KcApp/KcApp.tsx#L17) and [this](https://github.com/garronej/keycloakify-demo-app/blob/df664c13c77ce3c53ac7df0622d94d04e76d3f9f/src/KcApp/KcApp.tsx#L37) to update.
## v4
- Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳

View File

@ -1,6 +1,6 @@
{
"name": "keycloakify",
"version": "4.2.20",
"version": "4.4.0",
"description": "Keycloak theme generator for Reacts app",
"repository": {
"type": "git",
@ -77,11 +77,11 @@
},
"dependencies": {
"cheerio": "^1.0.0-rc.5",
"evt": "2.0.0-beta.38",
"evt": "2.0.0-beta.39",
"minimal-polyfills": "^2.2.1",
"path-browserify": "^1.0.1",
"react-markdown": "^5.0.3",
"scripting-tools": "^0.19.13",
"tsafe": "^0.8.1"
"tsafe": "^0.9.0"
}
}

View File

@ -16,7 +16,9 @@ export const pageIds = [
"terms.ftl",
"login-otp.ftl",
"login-update-profile.ftl",
"login-update-password.ftl",
"login-idp-link-confirm.ftl",
"login-page-expired.ftl",
] as const;
export type PageId = typeof pageIds[number];

View File

@ -10,8 +10,10 @@ import { LoginResetPassword } from "./LoginResetPassword";
import { LoginVerifyEmail } from "./LoginVerifyEmail";
import { Terms } from "./Terms";
import { LoginOtp } from "./LoginOtp";
import { LoginUpdatePassword } from "./LoginUpdatePassword";
import { LoginUpdateProfile } from "./LoginUpdateProfile";
import { LoginIdpLinkConfirm } from "./LoginIdpLinkConfirm";
import { LoginPageExpired } from "./LoginPageExpired";
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase } & KcProps) => {
switch (kcContext.pageId) {
@ -33,9 +35,13 @@ export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase }
return <Terms {...{ kcContext, ...props }} />;
case "login-otp.ftl":
return <LoginOtp {...{ kcContext, ...props }} />;
case "login-update-password.ftl":
return <LoginUpdatePassword {...{ kcContext, ...props }} />;
case "login-update-profile.ftl":
return <LoginUpdateProfile {...{ kcContext, ...props }} />;
case "login-idp-link-confirm.ftl":
return <LoginIdpLinkConfirm {...{ kcContext, ...props }} />;
case "login-page-expired.ftl":
return <LoginPageExpired {...{ kcContext, ...props }} />;
}
});

View File

@ -0,0 +1,36 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useKcMessage } from "../i18n/useKcMessage";
export const LoginPageExpired = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginPageExpired } & KcProps) => {
const { url } = kcContext;
const { msg } = useKcMessage();
return (
<Template
{...{ kcContext, ...props }}
doFetchDefaultThemeResources={true}
displayMessage={false}
headerNode={msg("pageExpiredTitle")}
formNode={
<>
<p id="instruction1" className="instruction">
{msg("pageExpiredMsg1")}
<a id="loginRestartLink" href={url.loginRestartFlowUrl}>
{msg("doClickHere")}
</a>{" "}
.<br />
{msg("pageExpiredMsg2")}{" "}
<a id="loginContinueLink" href={url.loginAction}>
{msg("doClickHere")}
</a>{" "}
.
</p>
</>
}
/>
);
});

View File

@ -0,0 +1,117 @@
import { memo } from "react";
import { Template } from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { useKcMessage } from "../i18n/useKcMessage";
import { useCssAndCx } from "tss-react";
export const LoginUpdatePassword = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginUpdatePassword } & KcProps) => {
const { cx } = useCssAndCx();
const { msg, msgStr } = useKcMessage();
const { url, messagesPerField, isAppInitiatedAction, username } = kcContext;
return (
<Template
{...{ kcContext, ...props }}
doFetchDefaultThemeResources={true}
headerNode={msg("updatePasswordTitle")}
formNode={
<form id="kc-passwd-update-form" className={cx(props.kcFormClass)} action={url.loginAction} method="post">
<input
type="text"
id="username"
name="username"
value={username}
readOnly={true}
autoComplete="username"
style={{ display: "none" }}
/>
<input type="password" id="password" name="password" autoComplete="current-password" style={{ display: "none" }} />
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password", props.kcFormGroupErrorClass))}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor="password-new" className={cx(props.kcLabelClass)}>
{msg("passwordNew")}
</label>
</div>
<div className={cx(props.kcInputWrapperClass)}>
<input
type="password"
id="password-new"
name="password-new"
autoFocus
autoComplete="new-password"
className={cx(props.kcInputClass)}
/>
</div>
</div>
<div className={cx(props.kcFormGroupClass, messagesPerField.printIfExists("password-confirm", props.kcFormGroupErrorClass))}>
<div className={cx(props.kcLabelWrapperClass)}>
<label htmlFor="password-confirm" className={cx(props.kcLabelClass)}>
{msg("passwordConfirm")}
</label>
</div>
<div className={cx(props.kcInputWrapperClass)}>
<input
type="password"
id="password-confirm"
name="password-confirm"
autoComplete="new-password"
className={cx(props.kcInputClass)}
/>
</div>
</div>
<div className={cx(props.kcFormGroupClass)}>
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
<div className={cx(props.kcFormOptionsWrapperClass)}>
{isAppInitiatedAction && (
<div className="checkbox">
<label>
<input type="checkbox" id="logout-sessions" name="logout-sessions" value="on" checked />
{msgStr("logoutOtherSessions")}
</label>
</div>
)}
</div>
</div>
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
{isAppInitiatedAction ? (
<>
<input
className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonLargeClass)}
type="submit"
defaultValue={msgStr("doSubmit")}
/>
<button
className={cx(props.kcButtonClass, props.kcButtonDefaultClass, props.kcButtonLargeClass)}
type="submit"
name="cancel-aia"
value="true"
>
{msg("doCancel")}
</button>
</>
) : (
<input
className={cx(
props.kcButtonClass,
props.kcButtonPrimaryClass,
props.kcButtonBlockClass,
props.kcButtonLargeClass,
)}
type="submit"
defaultValue={msgStr("doSubmit")}
/>
)}
</div>
</div>
</form>
}
/>
);
});

View File

@ -21,8 +21,10 @@ export type KcContextBase =
| KcContextBase.LoginVerifyEmail
| KcContextBase.Terms
| KcContextBase.LoginOtp
| KcContextBase.LoginUpdatePassword
| KcContextBase.LoginUpdateProfile
| KcContextBase.LoginIdpLinkConfirm;
| KcContextBase.LoginIdpLinkConfirm
| KcContextBase.LoginPageExpired;
export declare namespace KcContextBase {
export type Common = {
@ -192,6 +194,11 @@ export declare namespace KcContextBase {
};
};
export type LoginUpdatePassword = Common & {
pageId: "login-update-password.ftl";
username: string;
};
export type LoginUpdateProfile = Common & {
pageId: "login-update-profile.ftl";
user: {
@ -207,6 +214,10 @@ export declare namespace KcContextBase {
pageId: "login-idp-link-confirm.ftl";
idpAlias: string;
};
export type LoginPageExpired = Common & {
pageId: "login-page-expired.ftl";
};
}
export type Attribute = {

View File

@ -346,6 +346,11 @@ export const kcContextMocks: KcContextBase[] = [
],
},
}),
id<KcContextBase.LoginUpdatePassword>({
...kcContextCommonMock,
"pageId": "login-update-password.ftl",
"username": "anUsername",
}),
id<KcContextBase.LoginUpdateProfile>({
...kcContextCommonMock,
"pageId": "login-update-profile.ftl",

View File

@ -545,6 +545,15 @@ evt@2.0.0-beta.38:
run-exclusive "^2.2.14"
tsafe "^0.4.1"
evt@2.0.0-beta.39:
version "2.0.0-beta.39"
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.39.tgz#3c859a83b35940f7eecfb5f148f03b7cbf3fee51"
integrity sha512-XxJkaHrFWBrzjTbnr5LJYXkGkADsAXReZfq2lFu3Kf1iCEw5/5ibrdXu3bQdWW6xkZ8qwAHT3STU9zYcCl09BA==
dependencies:
minimal-polyfills "^2.2.1"
run-exclusive "^2.2.14"
tsafe "^0.4.1"
execa@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@ -1469,6 +1478,11 @@ tsafe@^0.8.1:
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.8.1.tgz#9af7e1540bc04313a82d60c98056a5017c8b086b"
integrity sha512-EfPjxQHzndQAV/uh0SMGP26Wg3dCuaw8dRv2VPEuGHen5qzg2oqsMvZw2wkQFkiMisZq2fm95m5lheimW2Fpvg==
tsafe@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-0.9.0.tgz#8394e5fdf81e690c97e2b8be4180a079a4a19bfb"
integrity sha512-wmbu8pI/xmW69b13HoS8WbTcSlRTDjIut9ACblBjVZVTk0vsMRXdoh1k1jMu5EzKNohBavKHhqNOOsccSR7XCA==
tslib@^1.9.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"