Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d24a8e99cc | ||
|
59d2d56091 | ||
|
8515b7060a | ||
|
a076b3f4d0 | ||
|
1e3240ef35 | ||
|
d767080dfe | ||
|
b228eda488 | ||
|
35f54964ce | ||
|
759b834ccc | ||
|
137e12cbbb | ||
|
57b08d9dea | ||
|
1dd7b673a1 | ||
|
b00ffc50c3 | ||
|
f9db40d33d | ||
|
4bc6a843d8 | ||
|
da3e7514f0 | ||
|
bc396bc41b | ||
|
947efe8d63 | ||
|
64189bf8fe | ||
|
400c630418 | ||
|
402360b436 | ||
|
9f001f1521 | ||
|
368e3a32c5 | ||
|
002e3d4b3d | ||
|
f94f9b51c9 | ||
|
055b15bd46 | ||
|
0e70b0b0de |
@ -345,6 +345,24 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"doc"
|
"doc"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "kodebach",
|
||||||
|
"name": "Klemens Böswirth",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/23529132?v=4",
|
||||||
|
"profile": "https://github.com/kodebach",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "wnmzzzz",
|
||||||
|
"name": "wnmzzzz",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/117174301?v=4",
|
||||||
|
"profile": "https://github.com/wnmzzzz",
|
||||||
|
"contributions": [
|
||||||
|
"test"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/garronej/keycloakify/actions">
|
<a href="https://github.com/garronej/keycloakify/actions">
|
||||||
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main">
|
<img src="https://github.com/keycloakify/keycloakify/actions/workflows/ci.yaml/badge.svg">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.npmjs.com/package/keycloakify">
|
<a href="https://www.npmjs.com/package/keycloakify">
|
||||||
<img src="https://img.shields.io/npm/dm/keycloakify">
|
<img src="https://img.shields.io/npm/dm/keycloakify">
|
||||||
@ -171,6 +171,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="http://t.me/AAT_L"><img src="https://avatars.githubusercontent.com/u/118743608?v=4?s=100" width="100px;" alt="Lesha"/><br /><sub><b>Lesha</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=EternalSide" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="http://t.me/AAT_L"><img src="https://avatars.githubusercontent.com/u/118743608?v=4?s=100" width="100px;" alt="Lesha"/><br /><sub><b>Lesha</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=EternalSide" title="Code">💻</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://blog.bacongobbler.com"><img src="https://avatars.githubusercontent.com/u/1360539?v=4?s=100" width="100px;" alt="Matthew Fisher"/><br /><sub><b>Matthew Fisher</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=bacongobbler" title="Documentation">📖</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://blog.bacongobbler.com"><img src="https://avatars.githubusercontent.com/u/1360539?v=4?s=100" width="100px;" alt="Matthew Fisher"/><br /><sub><b>Matthew Fisher</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=bacongobbler" title="Documentation">📖</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kodebach"><img src="https://avatars.githubusercontent.com/u/23529132?v=4?s=100" width="100px;" alt="Klemens Böswirth"/><br /><sub><b>Klemens Böswirth</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kodebach" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wnmzzzz"><img src="https://avatars.githubusercontent.com/u/117174301?v=4?s=100" width="100px;" alt="wnmzzzz"/><br /><sub><b>wnmzzzz</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=wnmzzzz" title="Tests">⚠️</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "11.8.17",
|
"version": "11.8.23",
|
||||||
"description": "Framework to create custom Keycloak UIs",
|
"description": "Framework to create custom Keycloak UIs",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -20,7 +20,7 @@ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_dele
|
|||||||
export async function command(params: { buildContext: BuildContext }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||||
commandName: "add-story",
|
commandName: "add-story",
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
|
@ -11,7 +11,7 @@ import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
|
|||||||
export async function command(params: { buildContext: BuildContext }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||||
commandName: "copy-keycloak-resources-to-public",
|
commandName: "copy-keycloak-resources-to-public",
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@ import { runPrettier, getIsPrettierAvailable } from "./tools/runPrettier";
|
|||||||
export async function command(params: { buildContext: BuildContext }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||||
commandName: "eject-page",
|
commandName: "eject-page",
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
|
@ -12,7 +12,7 @@ import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath"
|
|||||||
export async function command(params: { buildContext: BuildContext }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||||
commandName: "initialize-account-theme",
|
commandName: "initialize-account-theme",
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,7 @@ import { command as updateKcGenCommand } from "./update-kc-gen";
|
|||||||
export async function command(params: { buildContext: BuildContext }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||||
commandName: "initialize-admin-theme",
|
commandName: "initialize-admin-theme",
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
|
@ -17,8 +17,8 @@ import chalk from "chalk";
|
|||||||
export async function command(params: { buildContext: BuildContext }) {
|
export async function command(params: { buildContext: BuildContext }) {
|
||||||
const { buildContext } = params;
|
const { buildContext } = params;
|
||||||
|
|
||||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||||
commandName: "initialize-account-theme",
|
commandName: "initialize-email-theme",
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -13,13 +13,15 @@ import * as fs from "fs";
|
|||||||
|
|
||||||
assert<Equals<ApiVersion, "v1">>();
|
assert<Equals<ApiVersion, "v1">>();
|
||||||
|
|
||||||
export function maybeDelegateCommandToCustomHandler(params: {
|
export async function maybeDelegateCommandToCustomHandler(params: {
|
||||||
commandName: CommandName;
|
commandName: CommandName;
|
||||||
buildContext: BuildContext;
|
buildContext: BuildContext;
|
||||||
}): { hasBeenHandled: boolean } {
|
}): Promise<{ hasBeenHandled: boolean }> {
|
||||||
const { commandName, buildContext } = params;
|
const { commandName, buildContext } = params;
|
||||||
|
|
||||||
const nodeModulesBinDirPath = getNodeModulesBinDirPath();
|
const nodeModulesBinDirPath = await getNodeModulesBinDirPath({
|
||||||
|
packageJsonFilePath: buildContext.packageJsonFilePath
|
||||||
|
});
|
||||||
|
|
||||||
if (!fs.readdirSync(nodeModulesBinDirPath).includes(BIN_NAME)) {
|
if (!fs.readdirSync(nodeModulesBinDirPath).includes(BIN_NAME)) {
|
||||||
return { hasBeenHandled: false };
|
return { hasBeenHandled: false };
|
||||||
|
@ -45,12 +45,12 @@ export async function getExtensionModuleFileSourceCodeReadyToBeCopied(params: {
|
|||||||
`This file has been claimed for ownership from ${extensionModuleName} version ${extensionModuleVersion}.`,
|
`This file has been claimed for ownership from ${extensionModuleName} version ${extensionModuleVersion}.`,
|
||||||
`To relinquish ownership and restore this file to its original content, run the following command:`,
|
`To relinquish ownership and restore this file to its original content, run the following command:`,
|
||||||
``,
|
``,
|
||||||
`$ npx keycloakify own --path '${path}' --revert`
|
`$ npx keycloakify own --path "${path}" --revert`
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
`WARNING: Before modifying this file, run the following command:`,
|
`WARNING: Before modifying this file, run the following command:`,
|
||||||
``,
|
``,
|
||||||
`$ npx keycloakify own --path '${path}'`,
|
`$ npx keycloakify own --path "${path}"`,
|
||||||
``,
|
``,
|
||||||
`This file is provided by ${extensionModuleName} version ${extensionModuleVersion}.`,
|
`This file is provided by ${extensionModuleName} version ${extensionModuleVersion}.`,
|
||||||
`It was copied into your repository by the postinstall script: \`keycloakify sync-extensions\`.`
|
`It was copied into your repository by the postinstall script: \`keycloakify sync-extensions\`.`
|
||||||
|
@ -14,6 +14,8 @@ export function getAbsoluteAndInOsFormatPath(params: {
|
|||||||
|
|
||||||
let pathOut = pathIsh;
|
let pathOut = pathIsh;
|
||||||
|
|
||||||
|
pathOut = pathOut.replace(/^['"]/, "").replace(/['"]$/, "");
|
||||||
|
|
||||||
pathOut = pathOut.replace(/\//g, pathSep);
|
pathOut = pathOut.replace(/\//g, pathSep);
|
||||||
|
|
||||||
if (pathOut.startsWith("~")) {
|
if (pathOut.startsWith("~")) {
|
||||||
|
@ -1,10 +1,29 @@
|
|||||||
import { sep as pathSep } from "path";
|
import { sep as pathSep, dirname as pathDirname, join as pathJoin } from "path";
|
||||||
|
import { getThisCodebaseRootDirPath } from "./getThisCodebaseRootDirPath";
|
||||||
|
import { getInstalledModuleDirPath } from "./getInstalledModuleDirPath";
|
||||||
|
import { existsAsync } from "./fs.existsAsync";
|
||||||
|
import { z } from "zod";
|
||||||
|
import * as fs from "fs/promises";
|
||||||
|
import { assert, is, type Equals } from "tsafe/assert";
|
||||||
|
import { id } from "tsafe/id";
|
||||||
|
|
||||||
let cache: string | undefined = undefined;
|
let cache_bestEffort: string | undefined = undefined;
|
||||||
|
|
||||||
export function getNodeModulesBinDirPath() {
|
/** NOTE: Careful, this function can fail when the binary
|
||||||
if (cache !== undefined) {
|
* Used is not in the node_modules directory of the project
|
||||||
return cache;
|
* (for example when running tests with vscode extension we'll get
|
||||||
|
* '/Users/dylan/.vscode/extensions/vitest.explorer-1.16.0/dist/worker.js'
|
||||||
|
*
|
||||||
|
* instead of
|
||||||
|
* '/Users/joseph/.nvm/versions/node/v22.12.0/bin/node'
|
||||||
|
* or
|
||||||
|
* '/Users/joseph/github/keycloakify-starter/node_modules/.bin/vite'
|
||||||
|
*
|
||||||
|
* as the value of process.argv[1]
|
||||||
|
*/
|
||||||
|
function getNodeModulesBinDirPath_bestEffort() {
|
||||||
|
if (cache_bestEffort !== undefined) {
|
||||||
|
return cache_bestEffort;
|
||||||
}
|
}
|
||||||
|
|
||||||
const binPath = process.argv[1];
|
const binPath = process.argv[1];
|
||||||
@ -30,9 +49,122 @@ export function getNodeModulesBinDirPath() {
|
|||||||
segments.unshift(segment);
|
segments.unshift(segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!foundNodeModules) {
|
||||||
|
throw new Error(`Could not find node_modules in path ${binPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
const nodeModulesBinDirPath = segments.join(pathSep);
|
const nodeModulesBinDirPath = segments.join(pathSep);
|
||||||
|
|
||||||
cache = nodeModulesBinDirPath;
|
cache_bestEffort = nodeModulesBinDirPath;
|
||||||
|
|
||||||
return nodeModulesBinDirPath;
|
return nodeModulesBinDirPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cache_withPackageJsonFileDirPath:
|
||||||
|
| { packageJsonFilePath: string; nodeModulesBinDirPath: string }
|
||||||
|
| undefined = undefined;
|
||||||
|
|
||||||
|
async function getNodeModulesBinDirPath_withPackageJsonFileDirPath(params: {
|
||||||
|
packageJsonFilePath: string;
|
||||||
|
}): Promise<string> {
|
||||||
|
const { packageJsonFilePath } = params;
|
||||||
|
|
||||||
|
use_cache: {
|
||||||
|
if (cache_withPackageJsonFileDirPath === undefined) {
|
||||||
|
break use_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
cache_withPackageJsonFileDirPath.packageJsonFilePath !== packageJsonFilePath
|
||||||
|
) {
|
||||||
|
cache_withPackageJsonFileDirPath = undefined;
|
||||||
|
break use_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache_withPackageJsonFileDirPath.nodeModulesBinDirPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [...]node_modules/keycloakify
|
||||||
|
const installedModuleDirPath = await getInstalledModuleDirPath({
|
||||||
|
// Here it will always be "keycloakify" but since we are in tools/ we make something generic
|
||||||
|
moduleName: await (async () => {
|
||||||
|
type ParsedPackageJson = {
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const zParsedPackageJson = (() => {
|
||||||
|
type TargetType = ParsedPackageJson;
|
||||||
|
|
||||||
|
const zTargetType = z.object({
|
||||||
|
name: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
assert<Equals<z.infer<typeof zTargetType>, TargetType>>;
|
||||||
|
|
||||||
|
return id<z.ZodType<TargetType>>(zTargetType);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const parsedPackageJson = JSON.parse(
|
||||||
|
(
|
||||||
|
await fs.readFile(
|
||||||
|
pathJoin(getThisCodebaseRootDirPath(), "package.json")
|
||||||
|
)
|
||||||
|
).toString("utf8")
|
||||||
|
);
|
||||||
|
|
||||||
|
zParsedPackageJson.parse(parsedPackageJson);
|
||||||
|
|
||||||
|
assert(is<ParsedPackageJson>(parsedPackageJson));
|
||||||
|
|
||||||
|
return parsedPackageJson.name;
|
||||||
|
})(),
|
||||||
|
packageJsonDirPath: pathDirname(packageJsonFilePath)
|
||||||
|
});
|
||||||
|
|
||||||
|
const segments = installedModuleDirPath.split(pathSep);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const segment = segments.pop();
|
||||||
|
|
||||||
|
if (segment === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find .bin directory relative to ${packageJsonFilePath}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment !== "node_modules") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const candidate = pathJoin(segments.join(pathSep), segment, ".bin");
|
||||||
|
|
||||||
|
if (!(await existsAsync(candidate))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_withPackageJsonFileDirPath = {
|
||||||
|
packageJsonFilePath,
|
||||||
|
nodeModulesBinDirPath: candidate
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache_withPackageJsonFileDirPath.nodeModulesBinDirPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNodeModulesBinDirPath(params: {
|
||||||
|
packageJsonFilePath: string;
|
||||||
|
}): Promise<string>;
|
||||||
|
export function getNodeModulesBinDirPath(params: {
|
||||||
|
packageJsonFilePath: undefined;
|
||||||
|
}): string;
|
||||||
|
export function getNodeModulesBinDirPath(params: {
|
||||||
|
packageJsonFilePath: string | undefined;
|
||||||
|
}): string | Promise<string> {
|
||||||
|
const { packageJsonFilePath } = params ?? {};
|
||||||
|
|
||||||
|
return packageJsonFilePath === undefined
|
||||||
|
? getNodeModulesBinDirPath_bestEffort()
|
||||||
|
: getNodeModulesBinDirPath_withPackageJsonFileDirPath({ packageJsonFilePath });
|
||||||
|
}
|
||||||
|
@ -15,7 +15,9 @@ export async function getIsPrettierAvailable(): Promise<boolean> {
|
|||||||
return getIsPrettierAvailable.cache;
|
return getIsPrettierAvailable.cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeModulesBinDirPath = getNodeModulesBinDirPath();
|
const nodeModulesBinDirPath = getNodeModulesBinDirPath({
|
||||||
|
packageJsonFilePath: undefined
|
||||||
|
});
|
||||||
|
|
||||||
const prettierBinPath = pathJoin(nodeModulesBinDirPath, "prettier");
|
const prettierBinPath = pathJoin(nodeModulesBinDirPath, "prettier");
|
||||||
|
|
||||||
@ -50,10 +52,26 @@ export async function getPrettier(): Promise<PrettierAndConfigHash> {
|
|||||||
// So we do a sketchy eval to bypass ncc.
|
// So we do a sketchy eval to bypass ncc.
|
||||||
// We make sure to only do that when linking, otherwise we import properly.
|
// We make sure to only do that when linking, otherwise we import properly.
|
||||||
if (readThisNpmPackageVersion().startsWith("0.0.0")) {
|
if (readThisNpmPackageVersion().startsWith("0.0.0")) {
|
||||||
eval(
|
const prettierDirPath = pathResolve(
|
||||||
`${symToStr({ prettier })} = require("${pathResolve(pathJoin(getNodeModulesBinDirPath(), "..", "prettier"))}")`
|
pathJoin(
|
||||||
|
getNodeModulesBinDirPath({ packageJsonFilePath: undefined }),
|
||||||
|
"..",
|
||||||
|
"prettier"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isCJS = typeof module !== "undefined" && module.exports;
|
||||||
|
|
||||||
|
if (isCJS) {
|
||||||
|
eval(`${symToStr({ prettier })} = require("${prettierDirPath}")`);
|
||||||
|
} else {
|
||||||
|
prettier = await new Promise(_resolve => {
|
||||||
|
eval(
|
||||||
|
`import("file:///${pathJoin(prettierDirPath, "index.mjs").replace(/\\/g, "/")}").then(prettier => _resolve(prettier))`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
assert(!is<undefined>(prettier));
|
assert(!is<undefined>(prettier));
|
||||||
|
|
||||||
break import_prettier;
|
break import_prettier;
|
||||||
@ -64,7 +82,7 @@ export async function getPrettier(): Promise<PrettierAndConfigHash> {
|
|||||||
|
|
||||||
const configHash = await (async () => {
|
const configHash = await (async () => {
|
||||||
const configFilePath = await prettier.resolveConfigFile(
|
const configFilePath = await prettier.resolveConfigFile(
|
||||||
pathJoin(getNodeModulesBinDirPath(), "..")
|
pathJoin(getNodeModulesBinDirPath({ packageJsonFilePath: undefined }), "..")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (configFilePath === null) {
|
if (configFilePath === null) {
|
||||||
|
@ -19,7 +19,7 @@ export async function command(params: { buildContext: BuildContext }) {
|
|||||||
await command({ buildContext });
|
await command({ buildContext });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({
|
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
|
||||||
commandName: "update-kc-gen",
|
commandName: "update-kc-gen",
|
||||||
buildContext
|
buildContext
|
||||||
});
|
});
|
||||||
|
@ -90,7 +90,6 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps<
|
|||||||
{advancedMsg(attribute.annotations.inputHelperTextAfter)}
|
{advancedMsg(attribute.annotations.inputHelperTextAfter)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{AfterField !== undefined && (
|
{AfterField !== undefined && (
|
||||||
<AfterField
|
<AfterField
|
||||||
attribute={attribute}
|
attribute={attribute}
|
||||||
@ -107,6 +106,10 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps<
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{/* See: https://github.com/keycloak/keycloak/issues/38029 */}
|
||||||
|
{kcContext.locale !== undefined && formFieldStates.find(x => x.attribute.name === "locale") === undefined && (
|
||||||
|
<input type="hidden" name="locale" value={i18n.currentLanguage.languageTag} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,10 @@ export default function Info(props: PageProps<Extract<KcContext, { pageId: "info
|
|||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: kcSanitize(
|
__html: kcSanitize(
|
||||||
(() => {
|
(() => {
|
||||||
let html = message.summary;
|
let html = message.summary?.trim();
|
||||||
|
|
||||||
if (requiredActions) {
|
if (requiredActions) {
|
||||||
html += "<b>";
|
html += " <b>";
|
||||||
|
|
||||||
html += requiredActions.map(requiredAction => advancedMsgStr(`requiredAction.${requiredAction}`)).join(", ");
|
html += requiredActions.map(requiredAction => advancedMsgStr(`requiredAction.${requiredAction}`)).join(", ");
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment, useState } from "react";
|
||||||
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
|
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
|
||||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||||
@ -17,6 +17,8 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
|
|||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
kcContext={kcContext}
|
kcContext={kcContext}
|
||||||
@ -26,7 +28,16 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
|
|||||||
displayMessage={!messagesPerField.existsError("totp")}
|
displayMessage={!messagesPerField.existsError("totp")}
|
||||||
headerNode={msg("doLogIn")}
|
headerNode={msg("doLogIn")}
|
||||||
>
|
>
|
||||||
<form id="kc-otp-login-form" className={kcClsx("kcFormClass")} action={url.loginAction} method="post">
|
<form
|
||||||
|
id="kc-otp-login-form"
|
||||||
|
className={kcClsx("kcFormClass")}
|
||||||
|
action={url.loginAction}
|
||||||
|
onSubmit={() => {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
method="post"
|
||||||
|
>
|
||||||
{otpLogin.userOtpCredentials.length > 1 && (
|
{otpLogin.userOtpCredentials.length > 1 && (
|
||||||
<div className={kcClsx("kcFormGroupClass")}>
|
<div className={kcClsx("kcFormGroupClass")}>
|
||||||
<div className={kcClsx("kcInputWrapperClass")}>
|
<div className={kcClsx("kcInputWrapperClass")}>
|
||||||
@ -94,6 +105,7 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
|
|||||||
id="kc-login"
|
id="kc-login"
|
||||||
type="submit"
|
type="submit"
|
||||||
value={msgStr("doLogIn")}
|
value={msgStr("doLogIn")}
|
||||||
|
disabled={isSubmitting}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,7 +46,7 @@ export const WithRequiredActions: Story = {
|
|||||||
kcContext={{
|
kcContext={{
|
||||||
messageHeader: "Message header",
|
messageHeader: "Message header",
|
||||||
message: {
|
message: {
|
||||||
summary: "Required actions: "
|
summary: "Required actions:"
|
||||||
},
|
},
|
||||||
requiredActions: ["CONFIGURE_TOTP", "UPDATE_PROFILE", "VERIFY_EMAIL", "CUSTOM_ACTION"],
|
requiredActions: ["CONFIGURE_TOTP", "UPDATE_PROFILE", "VERIFY_EMAIL", "CUSTOM_ACTION"],
|
||||||
"x-keycloakify": {
|
"x-keycloakify": {
|
||||||
|
@ -62,3 +62,22 @@ export const WithPasswordConfirmError: Story = {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WithAppInitiatedAction:
|
||||||
|
* - Purpose: Tests when the update password action was triggered by an app.
|
||||||
|
* - Scenario: Simulates the case where the user presses a 'change password' button in an app and is redirected to Keycloak to change it.
|
||||||
|
* - Key Aspect: Ensures the 'Cancel' button is shown correctly, which displays only when the action is app initiated.
|
||||||
|
*/
|
||||||
|
export const WithAppInitiatedAction: Story = {
|
||||||
|
render: () => (
|
||||||
|
<KcPageStory
|
||||||
|
kcContext={{
|
||||||
|
url: {
|
||||||
|
loginAction: "/mock-login-action"
|
||||||
|
},
|
||||||
|
isAppInitiatedAction: true
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user