diff --git a/apps/admin-ui/cypress/e2e/realm_roles_test.spec.ts b/apps/admin-ui/cypress/e2e/realm_roles_test.spec.ts index f4f56d56e5..af94430dc8 100644 --- a/apps/admin-ui/cypress/e2e/realm_roles_test.spec.ts +++ b/apps/admin-ui/cypress/e2e/realm_roles_test.spec.ts @@ -251,7 +251,7 @@ describe("Realm roles test", () => { keyValue.fillKeyValue({ key: "one", value: "1" }).validateRows(2); keyValue.save(); masthead.checkNotificationMessage("The role has been saved", true); - keyValue.validateRows(1); + keyValue.validateRows(2); }); it("should add attribute multiple", () => { @@ -262,14 +262,14 @@ describe("Realm roles test", () => { .fillKeyValue({ key: "two", value: "2" }, 1) .fillKeyValue({ key: "three", value: "3" }, 2) .save() - .validateRows(3); + .validateRows(4); }); it("should delete attribute", () => { listingPage.itemExist(editRoleName).goToItemDetails(editRoleName); createRealmRolePage.goToAttributesTab(); - keyValue.deleteRow(1).save().validateRows(2); + keyValue.deleteRow(1).save().validateRows(3); }); }); }); diff --git a/apps/admin-ui/src/components/role-form/RoleForm.tsx b/apps/admin-ui/src/components/role-form/RoleForm.tsx index ae24a5cb8e..0534f8ac3d 100644 --- a/apps/admin-ui/src/components/role-form/RoleForm.tsx +++ b/apps/admin-ui/src/components/role-form/RoleForm.tsx @@ -5,12 +5,12 @@ import { PageSection, ValidatedOptions, } from "@patternfly/react-core"; -import type { SubmitHandler, UseFormMethods } from "react-hook-form"; +import { SubmitHandler, useWatch, UseFormMethods } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link, To } from "react-router-dom-v5-compat"; import { FormAccess } from "../form-access/FormAccess"; -import type { AttributeForm } from "../key-value-form/AttributeForm"; +import { AttributeForm } from "../key-value-form/AttributeForm"; import { KeycloakTextArea } from "../keycloak-text-area/KeycloakTextArea"; import { KeycloakTextInput } from "../keycloak-text-input/KeycloakTextInput"; import { ViewHeader } from "../view-header/ViewHeader"; @@ -24,7 +24,7 @@ export type RoleFormProps = { }; export const RoleForm = ({ - form: { handleSubmit, errors, register, getValues }, + form: { register, control, handleSubmit, errors }, onSubmit, cancelLink, role, @@ -32,6 +32,12 @@ export const RoleForm = ({ }: RoleFormProps) => { const { t } = useTranslation("roles"); + const roleName = useWatch({ + control, + defaultValue: undefined, + name: "name", + }); + return ( <> {!editMode && } @@ -86,7 +92,7 @@ export const RoleForm = ({ ? ValidatedOptions.error : ValidatedOptions.default } - isDisabled={getValues().name?.includes("default-roles")} + isDisabled={roleName?.includes("default-roles")} /> diff --git a/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx b/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx index ceb8f07625..12a9bec747 100644 --- a/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx +++ b/apps/admin-ui/src/realm-roles/RealmRoleTabs.tsx @@ -8,9 +8,8 @@ import { Tab, TabTitleText, } from "@patternfly/react-core"; -import { omit } from "lodash-es"; import { useState } from "react"; -import { SubmitHandler, useForm } from "react-hook-form"; +import { SubmitHandler, useForm, useWatch } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useLocation, useMatch, useNavigate } from "react-router-dom-v5-compat"; @@ -30,6 +29,7 @@ import { import { arrayToKeyValue, keyValueToArray, + KeyValueType, } from "../components/key-value-form/key-value-convert"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { PermissionsTab } from "../components/permission-tab/PermissionTab"; @@ -54,11 +54,10 @@ export default function RealmRoleTabs() { const form = useForm({ mode: "onChange", }); - const { setValue, reset } = form; + const { control, reset, setValue } = form; const navigate = useNavigate(); const { adminClient } = useAdminClient(); - const [role, setRole] = useState(); const { id, clientId } = useParams(); const { pathname } = useLocation(); @@ -66,12 +65,11 @@ export default function RealmRoleTabs() { const { realm: realmName } = useRealm(); const [key, setKey] = useState(0); + const [attributes, setAttributes] = useState(); const { profileInfo } = useServerInfo(); - const refresh = () => { - setKey(key + 1); - }; + const refresh = () => setKey(key + 1); const { addAlert, addError } = useAlerts(); @@ -84,6 +82,18 @@ export default function RealmRoleTabs() { }; }; + const roleName = useWatch({ + control, + defaultValue: undefined, + name: "name", + }); + + const composites = useWatch({ + control, + defaultValue: false, + name: "composite", + }); + const [realm, setRealm] = useState(); useFetch( @@ -103,36 +113,20 @@ export default function RealmRoleTabs() { const convertedRole = convert(role); reset(convertedRole); + setAttributes(convertedRole.attributes); setRealm(realm); - setRole(convertedRole); }, [key] ); const onSubmit: SubmitHandler = async (formValues) => { try { - if ( - formValues.attributes && - formValues.attributes[formValues.attributes.length - 1]?.key === "" - ) { - setValue( - "attributes", - formValues.attributes.slice(0, formValues.attributes.length - 1) - ); - } - const { attributes, ...rest } = formValues; - let roleRepresentation: RoleRepresentation = rest; + const roleRepresentation: RoleRepresentation = rest; roleRepresentation.name = roleRepresentation.name?.trim(); + roleRepresentation.attributes = keyValueToArray(attributes); - if (attributes) { - roleRepresentation.attributes = keyValueToArray(attributes); - } - roleRepresentation = { - ...omit(role!, "attributes"), - ...roleRepresentation, - }; if (!clientId) { await adminClient.roles.updateById({ id }, roleRepresentation); } else { @@ -142,7 +136,7 @@ export default function RealmRoleTabs() { ); } - setRole(convert(roleRepresentation)); + setAttributes(attributes); addAlert(t("roleSaveSuccess"), AlertVariant.success); } catch (error) { addError("roles:roleSaveError", error); @@ -201,7 +195,7 @@ export default function RealmRoleTabs() { const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "roles:roleDeleteConfirm", messageKey: t("roles:roleDeleteConfirmDialog", { - selectedRoleName: role?.name || t("createRole"), + selectedRoleName: roleName || t("createRole"), }), continueButtonLabel: "common:delete", continueButtonVariant: ButtonVariant.danger, @@ -212,7 +206,7 @@ export default function RealmRoleTabs() { } else { await adminClient.clients.delRole({ id: clientId, - roleName: role!.name!, + roleName: roleName!, }); } addAlert(t("roleDeletedSuccess"), AlertVariant.success); @@ -266,14 +260,14 @@ export default function RealmRoleTabs() { ] = useConfirmDialog({ titleKey: t("roles:removeAllAssociatedRoles") + "?", messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", { - name: role?.name || t("createRole"), + name: roleName || t("createRole"), }), continueButtonLabel: "common:delete", continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { try { const additionalRoles = await adminClient.roles.getCompositeRoles({ - id: role!.id!, + id, }); await adminClient.roles.delCompositeRoles({ id }, additionalRoles); addAlert( @@ -296,7 +290,7 @@ export default function RealmRoleTabs() { const addComposites = async (composites: RoleRepresentation[]) => { try { await adminClient.roles.createComposite( - { roleId: role?.id!, realm: realm!.realm }, + { roleId: id, realm: realm!.realm }, composites ); refresh(); @@ -307,9 +301,10 @@ export default function RealmRoleTabs() { } }; - const isDefaultRole = (name: string) => realm?.defaultRole!.name === name; + const isDefaultRole = (name: string | undefined) => + realm?.defaultRole!.name === name; - if (!realm || !role) { + if (!realm) { return ; } @@ -321,17 +316,17 @@ export default function RealmRoleTabs() { addComposites(rows.map((r) => r.role))} onClose={() => setOpen(false)} /> )} - {role.composite && ( + {composites && ( {t("associatedRolesText")}} {...associatedRolesTab} > addComposites(rows.map((r) => r.role))} /> )} - {!isDefaultRole(role.name!) && ( + {!isDefaultRole(roleName) && ( reset(role)} + reset={() => + setValue("attributes", attributes, { shouldDirty: false }) + } /> )} - {!isDefaultRole(role.name!) && ( + {!isDefaultRole(roleName) && ( {t("usersInRole")}} {...usersInRoleTab} @@ -401,7 +398,7 @@ export default function RealmRoleTabs() { title={{t("common:permissions")}} {...permissionsTab} > - + )}