diff --git a/js/README.md b/js/README.md index 74934d6e72..309dc14385 100644 --- a/js/README.md +++ b/js/README.md @@ -10,9 +10,7 @@ This directory contains the UIs and related libraries of the Keycloak project wr │ └── keycloak-server # Keycloak server for local development of UIs ├── libs │ ├── keycloak-admin-client # Keycloak Admin Client library for Keycloak REST API - │ ├── keycloak-js # Keycloak JS library for securing HTML5/JavaScript applications - │ └── keycloak-masthead # Keycloak Masthead library for an easy way to bring applications into the Keycloak ecosystem, allow users to access - │ # and manage security for those applications and manage authorization of resources + │ └── keycloak-js # Keycloak JS library for securing HTML5/JavaScript applications ├── ... ## Data processing diff --git a/js/apps/account-ui/package.json b/js/apps/account-ui/package.json index 458da37e9c..bd33e1da25 100644 --- a/js/apps/account-ui/package.json +++ b/js/apps/account-ui/package.json @@ -24,7 +24,6 @@ "i18next": "^23.11.2", "i18next-http-backend": "^2.5.0", "keycloak-js": "workspace:*", - "keycloak-masthead": "workspace:*", "lodash-es": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -50,7 +49,6 @@ "command": "vite --host", "dependencies": [ "../../libs/ui-shared:build", - "../../libs/keycloak-masthead:build", "../../libs/keycloak-js:build", "../../libs/keycloak-admin-client:build" ] @@ -59,7 +57,6 @@ "command": "vite preview", "dependencies": [ "../../libs/ui-shared:build", - "../../libs/keycloak-masthead:build", "../../libs/keycloak-js:build", "../../libs/keycloak-admin-client:build" ] @@ -68,7 +65,6 @@ "command": "vite build", "dependencies": [ "../../libs/ui-shared:build", - "../../libs/keycloak-masthead:build", "../../libs/keycloak-js:build", "../../libs/keycloak-admin-client:build" ] @@ -77,7 +73,6 @@ "command": "eslint .", "dependencies": [ "../../libs/ui-shared:build", - "../../libs/keycloak-masthead:build", "../../libs/keycloak-js:build", "../../libs/keycloak-admin-client:build" ] diff --git a/js/apps/account-ui/src/root/Header.tsx b/js/apps/account-ui/src/root/Header.tsx index 5abde32ab9..e7d05b21b3 100644 --- a/js/apps/account-ui/src/root/Header.tsx +++ b/js/apps/account-ui/src/root/Header.tsx @@ -1,15 +1,8 @@ import { Button } from "@patternfly/react-core"; import { ExternalLinkSquareAltIcon } from "@patternfly/react-icons"; -import { - KeycloakMasthead, - KeycloakProvider, - Translations, - TranslationsProvider, -} from "keycloak-masthead"; -import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useHref } from "react-router-dom"; -import { label } from "ui-shared"; +import { KeycloakMasthead, label } from "ui-shared"; import { environment } from "../environment"; import { joinPath } from "../utils/joinPath"; @@ -47,32 +40,19 @@ export const Header = () => { // User can indicate that he wants an internal URL by starting it with "/" const indexHref = logoUrl.startsWith("/") ? internalLogoHref : logoUrl; - const translations = useMemo( - () => ({ - avatar: t("avatar"), - fullName: t("fullName"), - manageAccount: t("manageAccount"), - signOut: t("signOut"), - unknownUser: t("unknownUser"), - }), - [t], - ); return ( - - - ]} - /> - - + ]} + /> ); }; diff --git a/js/libs/keycloak-masthead/package.json b/js/libs/keycloak-masthead/package.json deleted file mode 100644 index 2df1155964..0000000000 --- a/js/libs/keycloak-masthead/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "keycloak-masthead", - "type": "module", - "main": "./dist/keycloak-masthead.js", - "types": "./dist/keycloak-masthead.d.ts", - "exports": { - ".": { - "import": "./dist/keycloak-masthead.js", - "types": "./dist/keycloak-masthead.d.ts" - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "wireit", - "lint": "wireit" - }, - "wireit": { - "build": { - "command": "vite build", - "dependencies": [ - "../../libs/keycloak-js:build" - ], - "files": [ - "src/**", - "package.json", - "tsconfig.json", - "vite.config.ts" - ], - "output": [ - "dist/**" - ] - }, - "lint": { - "command": "eslint .", - "dependencies": [ - "../../libs/keycloak-js:build" - ] - } - }, - "dependencies": { - "@patternfly/react-core": "^5.2.3", - "@patternfly/react-styles": "^5.2.1", - "keycloak-js": "workspace:*", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@types/react": "^18.2.79", - "@types/react-dom": "^18.2.25", - "@vitejs/plugin-react-swc": "^3.6.0", - "rollup-plugin-peer-deps-external": "^2.2.4", - "vite": "^5.2.9", - "vite-plugin-checker": "^0.6.4", - "vite-plugin-dts": "^3.8.3" - } -} diff --git a/js/libs/keycloak-masthead/src/KeycloakContext.tsx b/js/libs/keycloak-masthead/src/KeycloakContext.tsx deleted file mode 100644 index 8b5a64b4c2..0000000000 --- a/js/libs/keycloak-masthead/src/KeycloakContext.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import Keycloak, { type KeycloakTokenParsed } from "keycloak-js"; -import { - createContext, - PropsWithChildren, - useContext, - useMemo, - useState, -} from "react"; - -type KeycloakProps = { - keycloak: Keycloak; - token?: KeycloakTokenParsed; - updateToken: () => void; -}; - -const KeycloakContext = createContext(undefined); - -export type KeycloakProviderProps = { - keycloak: Keycloak; -}; - -export const useKeycloak = () => useContext(KeycloakContext); - -export const KeycloakProvider = ({ - keycloak, - children, -}: PropsWithChildren) => { - const [token, setToken] = useState(keycloak.tokenParsed); - const context = useMemo( - () => ({ - keycloak, - token, - updateToken: async () => { - await keycloak.updateToken(-1); - setToken(keycloak.tokenParsed); - }, - }), - [keycloak, token], - ); - return ( - - {children} - - ); -}; diff --git a/js/libs/keycloak-masthead/src/main.ts b/js/libs/keycloak-masthead/src/main.ts deleted file mode 100644 index 3a66242aaa..0000000000 --- a/js/libs/keycloak-masthead/src/main.ts +++ /dev/null @@ -1,11 +0,0 @@ -export { default as KeycloakMasthead } from "./Masthead"; - -// Keycloak -export { KeycloakProvider, useKeycloak } from "./KeycloakContext"; -export type { KeycloakProviderProps } from "./KeycloakContext"; - -// Translation -export { defaultTranslations } from "./translation/translations"; -export type { Translations } from "./translation/translations"; -export { TranslationsProvider } from "./translation/TranslationsContext"; -export type { TranslationsProviderProps } from "./translation/TranslationsContext"; diff --git a/js/libs/keycloak-masthead/src/svg.d.ts b/js/libs/keycloak-masthead/src/svg.d.ts deleted file mode 100644 index 3a982e1598..0000000000 --- a/js/libs/keycloak-masthead/src/svg.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module "*.svg" { - const content: React.FC>; - export default content; -} diff --git a/js/libs/keycloak-masthead/src/translation/TranslationsContext.tsx b/js/libs/keycloak-masthead/src/translation/TranslationsContext.tsx deleted file mode 100644 index 5c57e1605c..0000000000 --- a/js/libs/keycloak-masthead/src/translation/TranslationsContext.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { createContext, PropsWithChildren, useContext } from "react"; - -import type { Translations } from "./translations"; -import { defaultTranslations } from "./translations"; - -const TranslationsContext = createContext(defaultTranslations); - -export const useTranslations = () => useContext(TranslationsContext); - -export type TranslationsProviderProps = { - translations: Translations; -}; - -export const TranslationsProvider = ({ - translations, - children, -}: PropsWithChildren) => { - return ( - - {children} - - ); -}; diff --git a/js/libs/keycloak-masthead/src/translation/translations.ts b/js/libs/keycloak-masthead/src/translation/translations.ts deleted file mode 100644 index 3c1fd67af1..0000000000 --- a/js/libs/keycloak-masthead/src/translation/translations.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const defaultTranslations = { - avatar: "Avatar", - fullName: "{{givenName}} {{familyName}}", - manageAccount: "Manage account", - signOut: "Sign out", - unknownUser: "Anonymous", -}; - -export type Translations = typeof defaultTranslations; diff --git a/js/libs/keycloak-masthead/src/translation/useTranslation.ts b/js/libs/keycloak-masthead/src/translation/useTranslation.ts deleted file mode 100644 index 50e2c18ac7..0000000000 --- a/js/libs/keycloak-masthead/src/translation/useTranslation.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useCallback, useMemo } from "react"; - -import { Translations } from "./translations"; -import { useTranslations } from "./TranslationsContext"; - -export type TranslateFunction = ( - key: keyof Translations, - args?: Record, -) => Translations[typeof key]; - -export const useTranslation = () => { - const translations = useTranslations(); - const translate = useCallback( - (key, args) => { - const translation = translations[key]; - - if (!args) { - return translation; - } - - return Object.entries(args).reduce( - (formatted, [key, value]) => formatted.replaceAll(`{{${key}}}`, value), - translation, - ); - }, - [translations], - ); - - return useMemo(() => ({ t: translate }), [translate]); -}; diff --git a/js/libs/keycloak-masthead/src/util.ts b/js/libs/keycloak-masthead/src/util.ts deleted file mode 100644 index 0d881eeba1..0000000000 --- a/js/libs/keycloak-masthead/src/util.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type KeycloakTokenParsed } from "keycloak-js"; - -import { TranslateFunction } from "./translation/useTranslation"; - -export function loggedInUserName( - token: KeycloakTokenParsed | undefined, - t: TranslateFunction, -) { - if (!token) { - return t("unknownUser"); - } - - const givenName = token.given_name; - const familyName = token.family_name; - const preferredUsername = token.preferred_username; - - if (givenName && familyName) { - return t("fullName", { givenName, familyName }); - } - - return givenName || familyName || preferredUsername || t("unknownUser"); -} diff --git a/js/libs/keycloak-masthead/tsconfig.json b/js/libs/keycloak-masthead/tsconfig.json deleted file mode 100644 index eeda960f6e..0000000000 --- a/js/libs/keycloak-masthead/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "include": ["src"], - "exclude": ["**/*.test.ts", "**/*.test.tsx"] -} diff --git a/js/libs/keycloak-masthead/vite.config.ts b/js/libs/keycloak-masthead/vite.config.ts deleted file mode 100644 index 5f820a9360..0000000000 --- a/js/libs/keycloak-masthead/vite.config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import react from "@vitejs/plugin-react-swc"; -import path from "node:path"; -import peerDepsExternal from "rollup-plugin-peer-deps-external"; -import { defineConfig } from "vite"; -import { checker } from "vite-plugin-checker"; -import dts from "vite-plugin-dts"; - -// https://vitejs.dev/config/ -export default defineConfig({ - build: { - target: "esnext", - lib: { - entry: path.resolve(__dirname, "src/main.ts"), - formats: ["es"], - }, - rollupOptions: { - plugins: [ - peerDepsExternal({ - includeDependencies: true, - }), - ], - }, - }, - plugins: [ - react(), - checker({ typescript: true }), - dts({ insertTypesEntry: true }), - ], -}); diff --git a/js/libs/ui-shared/package.json b/js/libs/ui-shared/package.json index 6effd9f9b7..4bca4b1306 100644 --- a/js/libs/ui-shared/package.json +++ b/js/libs/ui-shared/package.json @@ -43,7 +43,9 @@ "@keycloak/keycloak-admin-client": "workspace:*", "@patternfly/react-core": "^5.2.3", "@patternfly/react-icons": "^5.2.1", + "@patternfly/react-styles": "^5.2.1", "i18next": "^23.11.2", + "keycloak-js": "workspace:*", "lodash-es": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/js/libs/ui-shared/src/main.ts b/js/libs/ui-shared/src/main.ts index ff6ff9b324..192313337e 100644 --- a/js/libs/ui-shared/src/main.ts +++ b/js/libs/ui-shared/src/main.ts @@ -37,3 +37,4 @@ export { createNamedContext } from "./utils/createNamedContext"; export { isDefined } from "./utils/isDefined"; export { useRequiredContext } from "./utils/useRequiredContext"; export { useStoredState } from "./utils/useStoredState"; +export { default as KeycloakMasthead } from "./masthead/Masthead"; diff --git a/js/libs/keycloak-masthead/src/DefaultAvatar.tsx b/js/libs/ui-shared/src/masthead/DefaultAvatar.tsx similarity index 100% rename from js/libs/keycloak-masthead/src/DefaultAvatar.tsx rename to js/libs/ui-shared/src/masthead/DefaultAvatar.tsx diff --git a/js/libs/keycloak-masthead/src/KeycloakDropdown.tsx b/js/libs/ui-shared/src/masthead/KeycloakDropdown.tsx similarity index 100% rename from js/libs/keycloak-masthead/src/KeycloakDropdown.tsx rename to js/libs/ui-shared/src/masthead/KeycloakDropdown.tsx diff --git a/js/libs/keycloak-masthead/src/Masthead.tsx b/js/libs/ui-shared/src/masthead/Masthead.tsx similarity index 82% rename from js/libs/keycloak-masthead/src/Masthead.tsx rename to js/libs/ui-shared/src/masthead/Masthead.tsx index 21888e6c41..07dc5b6cde 100644 --- a/js/libs/keycloak-masthead/src/Masthead.tsx +++ b/js/libs/ui-shared/src/masthead/Masthead.tsx @@ -7,19 +7,38 @@ import { PageHeaderToolsGroup, PageHeaderToolsItem, } from "@patternfly/react-core/deprecated"; +import { TFunction } from "i18next"; +import Keycloak, { type KeycloakTokenParsed } from "keycloak-js"; import { ReactNode } from "react"; - -import { KeycloakDropdown } from "./KeycloakDropdown"; -import { useTranslation } from "./translation/useTranslation"; -import { loggedInUserName } from "./util"; +import { useTranslation } from "react-i18next"; import { DefaultAvatar } from "./DefaultAvatar"; -import { useKeycloak } from "./KeycloakContext"; +import { KeycloakDropdown } from "./KeycloakDropdown"; + +function loggedInUserName( + token: KeycloakTokenParsed | undefined, + t: TFunction, +) { + if (!token) { + return t("unknownUser"); + } + + const givenName = token.given_name; + const familyName = token.family_name; + const preferredUsername = token.preferred_username; + + if (givenName && familyName) { + return t("fullName", { givenName, familyName }); + } + + return givenName || familyName || preferredUsername || t("unknownUser"); +} type BrandLogo = BrandProps & { href: string; }; type KeycloakMastheadProps = PageHeaderProps & { + keycloak: Keycloak; brand: BrandLogo; avatar?: AvatarProps; features?: { @@ -33,6 +52,7 @@ type KeycloakMastheadProps = PageHeaderProps & { }; const KeycloakMasthead = ({ + keycloak, brand: { href: brandHref, ...brandProps }, avatar, features: { @@ -46,7 +66,6 @@ const KeycloakMasthead = ({ ...rest }: KeycloakMastheadProps) => { const { t } = useTranslation(); - const { keycloak } = useKeycloak()!; const extraItems = []; if (hasManageAccount) { extraItems.push( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 397591c876..dc777b0b79 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,9 +86,6 @@ importers: keycloak-js: specifier: workspace:* version: link:../../libs/keycloak-js - keycloak-masthead: - specifier: workspace:* - version: link:../../libs/keycloak-masthead lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -388,46 +385,6 @@ importers: specifier: ^0.3.4 version: 0.3.4 - js/libs/keycloak-masthead: - dependencies: - '@patternfly/react-core': - specifier: ^5.2.3 - version: 5.2.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) - '@patternfly/react-styles': - specifier: ^5.2.1 - version: 5.2.1 - keycloak-js: - specifier: workspace:* - version: link:../keycloak-js - react: - specifier: ^18.2.0 - version: 18.2.0 - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - devDependencies: - '@types/react': - specifier: ^18.2.79 - version: 18.2.79 - '@types/react-dom': - specifier: ^18.2.25 - version: 18.2.25 - '@vitejs/plugin-react-swc': - specifier: ^3.6.0 - version: 3.6.0(vite@5.2.9(@types/node@20.12.7)(lightningcss@1.24.1)(terser@5.30.3)) - rollup-plugin-peer-deps-external: - specifier: ^2.2.4 - version: 2.2.4(rollup@4.14.3) - vite: - specifier: ^5.2.9 - version: 5.2.9(@types/node@20.12.7)(lightningcss@1.24.1)(terser@5.30.3) - vite-plugin-checker: - specifier: ^0.6.4 - version: 0.6.4(eslint@8.57.0)(optionator@0.9.3)(typescript@5.4.5)(vite@5.2.9(@types/node@20.12.7)(lightningcss@1.24.1)(terser@5.30.3))(vue-tsc@1.8.27(typescript@5.4.5)) - vite-plugin-dts: - specifier: ^3.8.3 - version: 3.8.3(@types/node@20.12.7)(rollup@4.14.3)(typescript@5.4.5)(vite@5.2.9(@types/node@20.12.7)(lightningcss@1.24.1)(terser@5.30.3)) - js/libs/ui-shared: dependencies: '@keycloak/keycloak-admin-client': @@ -439,9 +396,15 @@ importers: '@patternfly/react-icons': specifier: ^5.2.1 version: 5.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@patternfly/react-styles': + specifier: ^5.2.1 + version: 5.2.1 i18next: specifier: ^23.11.2 version: 23.11.2 + keycloak-js: + specifier: workspace:* + version: link:../keycloak-js lodash-es: specifier: ^4.17.21 version: 4.17.21