From 05af84f20fe32bae6c829eb1fc87c24bede39557 Mon Sep 17 00:00:00 2001 From: Jenny <32821331+jenny-s51@users.noreply.github.com> Date: Tue, 21 Dec 2021 01:34:44 -0500 Subject: [PATCH] Edit message bundle values (#1663) * message bundles are now editable * update alert text in tests * delete realm settings test * remove updatedMessageBundles * remove unused usefetch * update lodash import * fix add locale test * fix add bundle * cypress tests * fix data test id on button * remove unused isAgtive prop * fix lint errors * unskip tests * refactor KDT to Table with PaginatedToolbar * unskip tests * uncomment * revert KDT * remove unused onSelect * Apply suggestions from code review Co-authored-by: Jon Koops * PR feedback * PR feedback * remove useEffect * fix localization test * rewrite localization test * switch test * unskip * force * PR feedback * package lock * remove true Co-authored-by: Jon Koops --- .../realm_settings_events_test.spec.ts | 21 +- .../realm_settings/RealmSettingsPage.ts | 5 +- package-lock.json | 489 +----------------- package.json | 2 +- src/realm-settings/LocalizationTab.tsx | 305 ++++++++--- src/realm-settings/messages.ts | 11 +- 6 files changed, 279 insertions(+), 554 deletions(-) diff --git a/cypress/integration/realm_settings_events_test.spec.ts b/cypress/integration/realm_settings_events_test.spec.ts index 254978be3f..641e56f92a 100644 --- a/cypress/integration/realm_settings_events_test.spec.ts +++ b/cypress/integration/realm_settings_events_test.spec.ts @@ -79,16 +79,11 @@ describe("Realm settings events tab tests", () => { }; const addBundle = () => { - const localizationUrl = `/auth/admin/realms/${realmName}/localization/en`; - cy.intercept(localizationUrl).as("localizationFetch"); - realmSettingsPage.addKeyValuePair( "key_" + (Math.random() + 1).toString(36).substring(7), "value_" + (Math.random() + 1).toString(36).substring(7) ); - cy.wait(["@localizationFetch"]); - return this; }; @@ -177,10 +172,24 @@ describe("Realm settings events tab tests", () => { cy.findByTestId("rs-localization-tab").click(); + cy.findByTestId("internationalization-disabled").click({ force: true }); + + cy.get(realmSettingsPage.supportedLocalesTypeahead) + .click() + .get(".pf-c-select__menu-item") + .contains("Dansk") + .click(); + + cy.get("#kc-l-supported-locales").click(); + + cy.findByTestId("localization-tab-save").click(); + + cy.findByTestId("add-bundle-button").click({ force: true }); + addBundle(); masthead.checkNotificationMessage( - "Success! The localization text has been created." + "Success! The message bundle has been added." ); }); diff --git a/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts b/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts index 0a751db0f2..a70f7a655c 100644 --- a/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts +++ b/cypress/support/pages/admin_console/manage/realm_settings/RealmSettingsPage.ts @@ -48,6 +48,9 @@ export default class RealmSettingsPage { selectDefaultLocale = "select-default-locale"; defaultLocaleList = "select-default-locale > div > ul"; + supportedLocalesTypeahead = + "#kc-l-supported-locales-select-multi-typeahead-typeahead"; + supportedLocalesToggle = "#kc-l-supported-locales"; emailSaveBtn = "email-tab-save"; managedAccessSwitch = "user-managed-access-switch"; userRegSwitch = "user-reg-switch"; @@ -75,7 +78,7 @@ export default class RealmSettingsPage { testConnectionButton = "test-connection-button"; modalTestConnectionButton = "modal-test-connection-button"; emailAddressInput = "email-address-input"; - addBundleButton = "no-message-bundles-empty-action"; + addBundleButton = "add-bundle-button"; confirmAddBundle = "add-bundle-confirm-button"; keyInput = "key-input"; valueInput = "value-input"; diff --git a/package-lock.json b/package-lock.json index 4726d342c6..62f15ababa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "name": "keycloak-admin-ui", "license": "Apache", "dependencies": { - "@keycloak/keycloak-admin-client": "^16.0.0-dev.61", + "@keycloak/keycloak-admin-client": "^16.0.0-dev.66", "@patternfly/patternfly": "^4.164.2", "@patternfly/react-code-editor": "^4.16.4", "@patternfly/react-core": "^4.175.4", @@ -3407,9 +3407,9 @@ } }, "node_modules/@keycloak/keycloak-admin-client": { - "version": "16.0.0-dev.61", - "resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-16.0.0-dev.61.tgz", - "integrity": "sha512-xeJTOQevOeHe8bQfu3/6Y9Lsort3Ep/VgzieUKmBHdR25kkMyQEUtZGX/7nQKYofjONG2m/KWivNwf0OZp+rhg==", + "version": "16.0.0-dev.66", + "resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-16.0.0-dev.66.tgz", + "integrity": "sha512-3roWy7XVltOHRq7vDihJDf5LFOgA1Hk1IhiokjWPEHD/Igmo8xn0b1ITgiNn4kHEnmP+7XDJNxhWEL/yFPIBHA==", "dependencies": { "axios": "^0.24.0", "camelize-ts": "^1.0.8", @@ -6739,16 +6739,6 @@ "node": ">=8" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bl": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", @@ -9243,90 +9233,6 @@ "esbuild-windows-arm64": "0.14.3" } }, - "node_modules/esbuild-android-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.3.tgz", - "integrity": "sha512-v/vdnGJiSGWOAXzg422T9qb4S+P3tOaYtc5n3FDR27Bh3/xQDS7PdYz/yY7HhOlVp0eGwWNbPHEi8FcEhXjsuw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/esbuild-darwin-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.3.tgz", - "integrity": "sha512-swY5OtEg6cfWdgc/XEjkBP7wXSyXXeZHEsWMdh1bDiN1D6GmRphk9SgKFKTj+P3ZHhOGIcC1+UdIwHk5bUcOig==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.3.tgz", - "integrity": "sha512-6i9dXPk8oT87wF6VHmwzSad76eMRU2Rt+GXrwF3Y4DCJgnPssJbabNQ9gurkuEX8M0YnEyJF0d1cR7rpTzcEiA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/esbuild-freebsd-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.3.tgz", - "integrity": "sha512-WDY5ENsmyceeE+95U3eI+FM8yARY5akWkf21M/x/+v2P5OVsYqCYELglSeAI5Y7bhteCVV3g4i2fRqtkmprdSA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.3.tgz", - "integrity": "sha512-4BEEGcP0wBzg04pCCWXlgaPuksQHHfwHvYgCIsi+7IsuB17ykt6MHhTkHR5b5pjI/jNtRhPfMsDODUyftQJgvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/esbuild-linux-32": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.3.tgz", - "integrity": "sha512-8yhsnjLG/GwCA1RAIndjmCHWViRB2Ol0XeOh2fCXS9qF8tlVrJB7qAiHZpm2vXx+yjOA/bFLTxzU+5pMKqkn5A==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, "node_modules/esbuild-linux-64": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.3.tgz", @@ -9341,146 +9247,6 @@ ], "peer": true }, - "node_modules/esbuild-linux-arm": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.3.tgz", - "integrity": "sha512-YcMvJHAQnWrWKb+eLxN9e/iWUC/3w01UF/RXuMknqOW3prX8UQ63QknWz9/RI8BY/sdrdgPEbSmsTU2jy2cayQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/esbuild-linux-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.3.tgz", - "integrity": "sha512-wPLyRoqoV/tEMQ7M24DpAmCMyKqBmtgZY35w2tXM8X5O5b2Ohi7fkPSmd6ZgLIxZIApWt88toA8RT0S7qoxcOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.3.tgz", - "integrity": "sha512-DdmfM5rcuoqjQL3px5MbquAjZWnySB5LdTrg52SSapp0gXMnGcsM6GY2WVta02CMKn5qi7WPVG4WbqTWE++tJw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.3.tgz", - "integrity": "sha512-ujdqryj0m135Ms9yaNDVFAcLeRtyftM/v2v7Osji5zElf2TivSMdFxdrYnYICuHfkm8c8gHg1ncwqitL0r+nnA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/esbuild-netbsd-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.3.tgz", - "integrity": "sha512-Z/UB9OUdwo1KDJCSGnVueDuKowRZRkduLvRMegHtDBHC3lS5LfZ3RdM1i+4MMN9iafyk8Q9FNcqIXI178ZujvA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "peer": true - }, - "node_modules/esbuild-openbsd-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.3.tgz", - "integrity": "sha512-9I1uoMDeogq3zQuTe3qygmXYjImnvc6rBn51LLbLniQDlfvqHPBMnAZ/5KshwtXXIIMkCwByytDZdiuzRRlTvQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "peer": true - }, - "node_modules/esbuild-sunos-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.3.tgz", - "integrity": "sha512-pldqx/Adxl4V4ymiyKxOOyJmHn6nUIo3wqk2xBx07iDgmL2XTcDDQd7N4U4QGu9LnYN4ZF+8IdOYa3oRRpbjtg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "peer": true - }, - "node_modules/esbuild-windows-32": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.3.tgz", - "integrity": "sha512-AqzvA/KbkC2m3kTXGpljLin3EttRbtoPTfBn6w6n2m9MWkTEbhQbE1ONoOBxhO5tExmyJdL/6B87TJJD5jEFBQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/esbuild-windows-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.3.tgz", - "integrity": "sha512-HGg3C6113zLGB5hN41PROTnBuoh/arG2lQdOird6xFl9giff1cAfMQOUJUfODKD57dDqHjQ1YGW8gOkg0/IrWw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/esbuild-windows-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.3.tgz", - "integrity": "sha512-qB2izYu4VpigGnOrAN2Yv7ICYLZWY/AojZtwFfteViDnHgW4jXPYkHQIXTISJbRz25H2cYiv+MfRQYK31RNjlw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -10605,13 +10371,6 @@ "node": ">=4" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -10979,20 +10738,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -15561,13 +15306,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "dev": true, - "optional": true - }, "node_modules/nanoid": { "version": "3.1.30", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", @@ -17949,21 +17687,6 @@ "@rollup/plugin-inject": "^4.0.0" } }, - "node_modules/rollup/node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -21063,25 +20786,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack-chokidar2/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/watchpack-chokidar2/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -24141,9 +23845,9 @@ } }, "@keycloak/keycloak-admin-client": { - "version": "16.0.0-dev.61", - "resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-16.0.0-dev.61.tgz", - "integrity": "sha512-xeJTOQevOeHe8bQfu3/6Y9Lsort3Ep/VgzieUKmBHdR25kkMyQEUtZGX/7nQKYofjONG2m/KWivNwf0OZp+rhg==", + "version": "16.0.0-dev.66", + "resolved": "https://registry.npmjs.org/@keycloak/keycloak-admin-client/-/keycloak-admin-client-16.0.0-dev.66.tgz", + "integrity": "sha512-3roWy7XVltOHRq7vDihJDf5LFOgA1Hk1IhiokjWPEHD/Igmo8xn0b1ITgiNn4kHEnmP+7XDJNxhWEL/yFPIBHA==", "requires": { "axios": "^0.24.0", "camelize-ts": "^1.0.8", @@ -26915,16 +26619,6 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, "bl": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", @@ -28916,54 +28610,6 @@ "esbuild-windows-arm64": "0.14.3" } }, - "esbuild-android-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.3.tgz", - "integrity": "sha512-v/vdnGJiSGWOAXzg422T9qb4S+P3tOaYtc5n3FDR27Bh3/xQDS7PdYz/yY7HhOlVp0eGwWNbPHEi8FcEhXjsuw==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-darwin-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.3.tgz", - "integrity": "sha512-swY5OtEg6cfWdgc/XEjkBP7wXSyXXeZHEsWMdh1bDiN1D6GmRphk9SgKFKTj+P3ZHhOGIcC1+UdIwHk5bUcOig==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-darwin-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.3.tgz", - "integrity": "sha512-6i9dXPk8oT87wF6VHmwzSad76eMRU2Rt+GXrwF3Y4DCJgnPssJbabNQ9gurkuEX8M0YnEyJF0d1cR7rpTzcEiA==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-freebsd-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.3.tgz", - "integrity": "sha512-WDY5ENsmyceeE+95U3eI+FM8yARY5akWkf21M/x/+v2P5OVsYqCYELglSeAI5Y7bhteCVV3g4i2fRqtkmprdSA==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-freebsd-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.3.tgz", - "integrity": "sha512-4BEEGcP0wBzg04pCCWXlgaPuksQHHfwHvYgCIsi+7IsuB17ykt6MHhTkHR5b5pjI/jNtRhPfMsDODUyftQJgvw==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-linux-32": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.3.tgz", - "integrity": "sha512-8yhsnjLG/GwCA1RAIndjmCHWViRB2Ol0XeOh2fCXS9qF8tlVrJB7qAiHZpm2vXx+yjOA/bFLTxzU+5pMKqkn5A==", - "dev": true, - "optional": true, - "peer": true - }, "esbuild-linux-64": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.3.tgz", @@ -28972,86 +28618,6 @@ "optional": true, "peer": true }, - "esbuild-linux-arm": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.3.tgz", - "integrity": "sha512-YcMvJHAQnWrWKb+eLxN9e/iWUC/3w01UF/RXuMknqOW3prX8UQ63QknWz9/RI8BY/sdrdgPEbSmsTU2jy2cayQ==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-linux-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.3.tgz", - "integrity": "sha512-wPLyRoqoV/tEMQ7M24DpAmCMyKqBmtgZY35w2tXM8X5O5b2Ohi7fkPSmd6ZgLIxZIApWt88toA8RT0S7qoxcOA==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-linux-mips64le": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.3.tgz", - "integrity": "sha512-DdmfM5rcuoqjQL3px5MbquAjZWnySB5LdTrg52SSapp0gXMnGcsM6GY2WVta02CMKn5qi7WPVG4WbqTWE++tJw==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-linux-ppc64le": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.3.tgz", - "integrity": "sha512-ujdqryj0m135Ms9yaNDVFAcLeRtyftM/v2v7Osji5zElf2TivSMdFxdrYnYICuHfkm8c8gHg1ncwqitL0r+nnA==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-netbsd-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.3.tgz", - "integrity": "sha512-Z/UB9OUdwo1KDJCSGnVueDuKowRZRkduLvRMegHtDBHC3lS5LfZ3RdM1i+4MMN9iafyk8Q9FNcqIXI178ZujvA==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-openbsd-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.3.tgz", - "integrity": "sha512-9I1uoMDeogq3zQuTe3qygmXYjImnvc6rBn51LLbLniQDlfvqHPBMnAZ/5KshwtXXIIMkCwByytDZdiuzRRlTvQ==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-sunos-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.3.tgz", - "integrity": "sha512-pldqx/Adxl4V4ymiyKxOOyJmHn6nUIo3wqk2xBx07iDgmL2XTcDDQd7N4U4QGu9LnYN4ZF+8IdOYa3oRRpbjtg==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-windows-32": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.3.tgz", - "integrity": "sha512-AqzvA/KbkC2m3kTXGpljLin3EttRbtoPTfBn6w6n2m9MWkTEbhQbE1ONoOBxhO5tExmyJdL/6B87TJJD5jEFBQ==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-windows-64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.3.tgz", - "integrity": "sha512-HGg3C6113zLGB5hN41PROTnBuoh/arG2lQdOird6xFl9giff1cAfMQOUJUfODKD57dDqHjQ1YGW8gOkg0/IrWw==", - "dev": true, - "optional": true, - "peer": true - }, - "esbuild-windows-arm64": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.3.tgz", - "integrity": "sha512-qB2izYu4VpigGnOrAN2Yv7ICYLZWY/AojZtwFfteViDnHgW4jXPYkHQIXTISJbRz25H2cYiv+MfRQYK31RNjlw==", - "dev": true, - "optional": true, - "peer": true - }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -29911,13 +29477,6 @@ "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", "dev": true }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -30182,13 +29741,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -33639,13 +33191,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "dev": true, - "optional": true - }, "nanoid": { "version": "3.1.30", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz", @@ -35484,15 +35029,6 @@ "dev": true, "requires": { "fsevents": "~2.1.2" - }, - "dependencies": { - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - } } }, "rollup-plugin-polyfill-node": { @@ -37942,17 +37478,6 @@ } } }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } - }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", diff --git a/package.json b/package.json index b197952f19..cd36b37dc7 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "prepare": "husky install" }, "dependencies": { - "@keycloak/keycloak-admin-client": "^16.0.0-dev.61", + "@keycloak/keycloak-admin-client": "^16.0.0-dev.66", "@patternfly/patternfly": "^4.164.2", "@patternfly/react-code-editor": "^4.16.4", "@patternfly/react-core": "^4.175.4", diff --git a/src/realm-settings/LocalizationTab.tsx b/src/realm-settings/LocalizationTab.tsx index be8490b296..6dd7a92215 100644 --- a/src/realm-settings/LocalizationTab.tsx +++ b/src/realm-settings/LocalizationTab.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; import { useTranslation } from "react-i18next"; +import { cloneDeep, isEqual, uniqWith } from "lodash"; import { Controller, useForm, useFormContext, useWatch } from "react-hook-form"; import { ActionGroup, @@ -21,14 +22,32 @@ import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/r import { FormAccess } from "../components/form-access/FormAccess"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { FormPanel } from "../components/scroll-form/FormPanel"; -import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; -import { useAdminClient } from "../context/auth/AdminClient"; +import { useAdminClient, useFetch } from "../context/auth/AdminClient"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { AddMessageBundleModal } from "./AddMessageBundleModal"; import { useAlerts } from "../components/alert/Alerts"; import { HelpItem } from "../components/help-enabler/HelpItem"; import { useRealm } from "../context/realm-context/RealmContext"; import { DEFAULT_LOCALE } from "../i18n"; +import { + EditableTextCell, + validateCellEdits, + cancelCellEdits, + applyCellEdits, + RowErrors, + RowEditType, + IRowCell, + TableBody, + TableHeader, + Table, + TableVariant, + IRow, + IEditableTextCell, +} from "@patternfly/react-table"; +import type { EditableTextCellProps } from "@patternfly/react-table/dist/esm/components/Table/base"; +import { PaginatingTableToolbar } from "../components/table-toolbar/PaginatingTableToolbar"; +import { SearchIcon } from "@patternfly/react-icons"; +import { useWhoAmI } from "../context/whoami/WhoAmI"; type LocalizationTabProps = { save: (realm: RealmRepresentation) => void; @@ -39,6 +58,12 @@ type LocalizationTabProps = { export type KeyValueType = { key: string; value: string }; +export enum RowEditAction { + Save = "save", + Cancel = "cancel", + Edit = "edit", +} + export type BundleForm = { messageBundle: KeyValueType; }; @@ -60,11 +85,14 @@ export const LocalizationTab = ({ const { getValues, control, handleSubmit, formState } = useFormContext(); const [selectMenuValueSelected, setSelectMenuValueSelected] = useState(false); + const [messageBundles, setMessageBundles] = useState<[string, string][]>([]); + const [tableRows, setTableRows] = useState([]); const themeTypes = useServerInfo().themes!; const bundleForm = useForm({ mode: "onChange" }); const { addAlert, addError } = useAlerts(); const { realm: currentRealm } = useRealm(); + const { whoAmI } = useWhoAmI(); const watchSupportedLocales = useWatch({ control, @@ -76,31 +104,170 @@ export const LocalizationTab = ({ defaultValue: false, }); - const loader = async () => { - try { - const result = await adminClient.realms.getRealmLocalizationTexts({ - realm: realm.realm!, - selectedLocale: - selectMenuLocale || getValues("defaultLocale") || DEFAULT_LOCALE, - }); + const [tableKey, setTableKey] = useState(0); + const [max, setMax] = useState(10); + const [first, setFirst] = useState(0); + const [filter, setFilter] = useState(""); - return Object.entries(result); - } catch (error) { - return []; - } + const refreshTable = () => { + setTableKey(tableKey + 1); }; - const tableLoader = async () => { - try { - const result = await adminClient.realms.getRealmLocalizationTexts({ - realm: currentRealm, - selectedLocale: selectMenuLocale, + useFetch( + async () => { + let result = await adminClient.realms.getRealmLocalizationTexts({ + first, + max, + realm: realm.realm!, + selectedLocale: + selectMenuLocale || getValues("defaultLocale") || whoAmI.getLocale(), }); - return Object.entries(result); - } catch (error) { - return []; + const searchInBundles = (idx: number) => { + return Object.entries(result).filter((i) => i[idx].includes(filter)); + }; + + if (filter) { + const filtered = uniqWith( + searchInBundles(0).concat(searchInBundles(1)), + isEqual + ); + + result = Object.fromEntries(filtered); + } + + return { result }; + }, + ({ result }) => { + const bundles = Object.entries(result).slice(first, first + max + 1); + setMessageBundles(bundles); + + const updatedRows = bundles.map((messageBundle) => ({ + rowEditBtnAriaLabel: () => + t("rowEditBtnAriaLabel", { + messageBundle: messageBundle[1], + }), + rowSaveBtnAriaLabel: () => + t("rowSaveBtnAriaLabel", { + messageBundle: messageBundle[1], + }), + rowCancelBtnAriaLabel: () => + t("rowCancelBtnAriaLabel", { + messageBundle: messageBundle[1], + }), + cells: [ + { + title: ( + value: string, + rowIndex: number, + cellIndex: number, + props + ) => ( + + ), + props: { + value: messageBundle[0], + }, + }, + { + title: ( + value: string, + rowIndex: number, + cellIndex: number, + props: EditableTextCellProps + ) => ( + + ), + props: { + value: messageBundle[1], + }, + }, + ], + })); + setTableRows(updatedRows); + + return bundles; + }, + [tableKey, filter, first, max] + ); + + const handleTextInputChange = ( + newValue: string, + evt: any, + rowIndex: number, + cellIndex: number + ) => { + setTableRows((prev) => { + const newRows = cloneDeep(prev); + ( + newRows[rowIndex]?.cells?.[cellIndex] as IEditableTextCell + ).props.editableValue = newValue; + return newRows; + }); + }; + + const updateEditableRows = async ( + evt: any, + type: RowEditType, + isEditable?: boolean | undefined, + rowIndex?: number, + validationErrors?: RowErrors + ) => { + if (rowIndex === undefined) { + return; } + const newRows = cloneDeep(tableRows); + let newRow: IRow; + const invalid = + !!validationErrors && Object.keys(validationErrors).length > 0; + + if (invalid) { + newRow = validateCellEdits(newRows[rowIndex], type, validationErrors); + } else if (type === RowEditAction.Cancel) { + newRow = cancelCellEdits(newRows[rowIndex]); + } else { + newRow = applyCellEdits(newRows[rowIndex], type); + } + newRows[rowIndex] = newRow; + + // Update the copy of the retrieved data set so we can save it when the user saves changes + + if (!invalid && type === RowEditAction.Save) { + const key = (newRow.cells?.[0] as IRowCell).props.value; + const value = (newRow.cells?.[1] as IRowCell).props.value; + + // We only have one editable value, otherwise we'd need to save each + try { + await adminClient.realms.addLocalization( + { + realm: realm.realm!, + selectedLocale: + selectMenuLocale || getValues("defaultLocale") || DEFAULT_LOCALE, + key, + }, + value + ); + addAlert(t("updateMessageBundleSuccess"), AlertVariant.success); + } catch (error) { + addAlert(t("updateMessageBundleError"), AlertVariant.danger); + } + } + setTableRows(newRows); }; const handleModalToggle = () => { @@ -129,17 +296,8 @@ export const LocalizationTab = ({ , ]; - const [tableKey, setTableKey] = useState(0); - - const refreshTable = () => { - setTableKey(new Date().getTime()); - }; - const addKeyValue = async (pair: KeyValueType): Promise => { try { - adminClient.setConfig({ - requestConfig: { headers: { "Content-Type": "text/plain" } }, - }); await adminClient.realms.addLocalization( { realm: currentRealm!, @@ -154,9 +312,9 @@ export const LocalizationTab = ({ realmName: currentRealm!, }); refreshTable(); - addAlert(t("pairCreatedSuccess"), AlertVariant.success); + addAlert(t("addMessageBundleSuccess"), AlertVariant.success); } catch (error) { - addError("realm-settings:pairCreatedError", error); + addError(t("addMessageBundleError"), error); } }; @@ -331,10 +489,32 @@ export const LocalizationTab = ({ {t("messageBundleDescription")}
- { + setFirst(first); + setMax(max); + }} + inputGroupName={"common:search"} + inputGroupOnEnter={(search) => { + setFilter(search); + setFirst(0); + setMax(10); + }} + inputGroupPlaceholder={t("searchForMessageBundle")} + toolbarItem={ + + } searchTypeComponent={