Port role form to new form controls (#27626)

Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Jon Koops 2024-03-07 15:08:36 +01:00 committed by GitHub
parent ea4155bbcd
commit 0adc842ac7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 129 additions and 155 deletions

View file

@ -94,7 +94,7 @@ export default class ProviderPage {
#rolesTab = "rolesTab"; #rolesTab = "rolesTab";
#createRoleBtn = "no-roles-for-this-client-empty-action"; #createRoleBtn = "no-roles-for-this-client-empty-action";
#roleSaveBtn = "save"; #roleSaveBtn = "save";
#roleNameField = "#kc-name"; #roleNameField = "name";
#groupName = "aa-uf-mappers-group"; #groupName = "aa-uf-mappers-group";
#clientName = "aa-uf-mappers-client"; #clientName = "aa-uf-mappers-client";
@ -310,7 +310,7 @@ export default class ProviderPage {
cy.wait(1000); cy.wait(1000);
cy.findByTestId(this.#createRoleBtn).click(); cy.findByTestId(this.#createRoleBtn).click();
cy.wait(1000); cy.wait(1000);
cy.get(this.#roleNameField).clear().type(roleName); cy.findByTestId(this.#roleNameField).clear().type(roleName);
cy.wait(1000); cy.wait(1000);
cy.findByTestId(this.#roleSaveBtn).click(); cy.findByTestId(this.#roleSaveBtn).click();
cy.wait(1000); cy.wait(1000);

View file

@ -1,16 +1,16 @@
class CreateRealmRolePage { class CreateRealmRolePage {
#realmRoleNameInput = "#kc-name"; #realmRoleNameInput = "name";
#realmRoleNameError = "#kc-name-helper"; #realmRoleNameError = "#name-helper";
#realmRoleDescriptionInput = "#kc-description"; #realmRoleDescriptionInput = "description";
#saveBtn = "save"; #saveBtn = "save";
#cancelBtn = "cancel"; #cancelBtn = "cancel";
//#region General Settings //#region General Settings
fillRealmRoleData(name: string, description = "") { fillRealmRoleData(name: string, description = "") {
cy.get(this.#realmRoleNameInput).clear(); cy.findByTestId(this.#realmRoleNameInput).clear();
if (name) { if (name) {
cy.get(this.#realmRoleNameInput).type(name); cy.findByTestId(this.#realmRoleNameInput).type(name);
} }
if (description !== "") { if (description !== "") {
@ -36,7 +36,7 @@ class CreateRealmRolePage {
} }
checkNameDisabled() { checkNameDisabled() {
cy.get(this.#realmRoleNameInput).should( cy.findByTestId(this.#realmRoleNameInput).should(
"have.attr", "have.attr",
"readonly", "readonly",
"readonly", "readonly",
@ -45,13 +45,16 @@ class CreateRealmRolePage {
} }
checkDescription(description: string) { checkDescription(description: string) {
cy.get(this.#realmRoleDescriptionInput).should("have.value", description); cy.findByTestId(this.#realmRoleDescriptionInput).should(
"have.value",
description,
);
return this; return this;
} }
updateDescription(description: string) { updateDescription(description: string) {
cy.get(this.#realmRoleDescriptionInput).clear(); cy.findByTestId(this.#realmRoleDescriptionInput).clear();
cy.get(this.#realmRoleDescriptionInput).type(description); cy.findByTestId(this.#realmRoleDescriptionInput).type(description);
return this; return this;
} }

View file

@ -1,6 +1,6 @@
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import { AlertVariant } from "@patternfly/react-core"; import { AlertVariant } from "@patternfly/react-core";
import { SubmitHandler, useForm } from "react-hook-form"; import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
@ -54,16 +54,17 @@ export default function CreateClientRole() {
}; };
return ( return (
<RoleForm <FormProvider {...form}>
form={form} <RoleForm
onSubmit={onSubmit} onSubmit={onSubmit}
cancelLink={toClient({ cancelLink={toClient({
realm, realm,
clientId: clientId!, clientId: clientId!,
tab: "roles", tab: "roles",
})} })}
role="manage-clients" role="manage-clients"
editMode={false} editMode={false}
/> />
</FormProvider>
); );
} }

View file

@ -1,22 +1,14 @@
import { import { ActionGroup, Button, PageSection } from "@patternfly/react-core";
ActionGroup, import { SubmitHandler, useFormContext, useWatch } from "react-hook-form";
Button,
FormGroup,
PageSection,
ValidatedOptions,
} from "@patternfly/react-core";
import { SubmitHandler, UseFormReturn, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link, To } from "react-router-dom"; import { Link, To } from "react-router-dom";
import { TextAreaControl, TextControl } from "ui-shared";
import { FormAccess } from "../form/FormAccess"; import { FormAccess } from "../form/FormAccess";
import { 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"; import { ViewHeader } from "../view-header/ViewHeader";
export type RoleFormProps = { export type RoleFormProps = {
form: UseFormReturn<AttributeForm>;
onSubmit: SubmitHandler<AttributeForm>; onSubmit: SubmitHandler<AttributeForm>;
cancelLink: To; cancelLink: To;
role: "manage-realm" | "manage-clients"; role: "manage-realm" | "manage-clients";
@ -24,18 +16,13 @@ export type RoleFormProps = {
}; };
export const RoleForm = ({ export const RoleForm = ({
form: {
register,
control,
handleSubmit,
formState: { errors },
},
onSubmit, onSubmit,
cancelLink, cancelLink,
role, role,
editMode, editMode,
}: RoleFormProps) => { }: RoleFormProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { control, handleSubmit } = useFormContext<AttributeForm>();
const roleName = useWatch({ const roleName = useWatch({
control, control,
@ -53,54 +40,30 @@ export const RoleForm = ({
role={role} role={role}
className="pf-u-mt-lg" className="pf-u-mt-lg"
> >
<FormGroup <TextControl
name="name"
label={t("roleName")} label={t("roleName")}
fieldId="kc-name" rules={{
validated={ required: !editMode ? t("required") : undefined,
errors.name ? ValidatedOptions.error : ValidatedOptions.default validate(value) {
} if (!value?.trim()) {
helperTextInvalid={t("required")} return t("required");
isRequired={!editMode} }
> },
<KeycloakTextInput }}
id="kc-name" readOnly={editMode}
isReadOnly={editMode} />
{...register("name", { <TextAreaControl
required: !editMode, name="description"
validate: (value) => {
if (!value?.trim()) {
return t("required").toString();
}
},
})}
/>
</FormGroup>
<FormGroup
label={t("description")} label={t("description")}
fieldId="kc-description" rules={{
validated={ maxLength: {
errors.description value: 255,
? ValidatedOptions.error message: t("maxLength", { length: 255 }),
: ValidatedOptions.default },
} }}
helperTextInvalid={errors.description?.message} isDisabled={roleName?.includes("default-roles") ?? false}
> />
<KeycloakTextArea
id="kc-description"
validated={
errors.description
? ValidatedOptions.error
: ValidatedOptions.default
}
isDisabled={roleName?.includes("default-roles")}
{...register("description", {
maxLength: {
value: 255,
message: t("maxLength", { length: 255 }),
},
})}
/>
</FormGroup>
<ActionGroup> <ActionGroup>
<Button data-testid="save" type="submit" variant="primary"> <Button data-testid="save" type="submit" variant="primary">
{t("save")} {t("save")}

View file

@ -1,6 +1,6 @@
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation"; import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import { AlertVariant } from "@patternfly/react-core"; import { AlertVariant } from "@patternfly/react-core";
import { SubmitHandler, useForm } from "react-hook-form"; import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -45,12 +45,13 @@ export default function CreateRealmRole() {
}; };
return ( return (
<RoleForm <FormProvider {...form}>
form={form} <RoleForm
onSubmit={onSubmit} onSubmit={onSubmit}
cancelLink={toRealmRoles({ realm })} cancelLink={toRealmRoles({ realm })}
role="manage-realm" role="manage-realm"
editMode={false} editMode={false}
/> />
</FormProvider>
); );
} }

View file

@ -9,7 +9,12 @@ import {
TabTitleText, TabTitleText,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { useState } from "react"; import { useState } from "react";
import { SubmitHandler, useForm, useWatch } from "react-hook-form"; import {
FormProvider,
SubmitHandler,
useForm,
useWatch,
} from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useLocation, useMatch, useNavigate } from "react-router-dom"; import { useLocation, useMatch, useNavigate } from "react-router-dom";
@ -333,71 +338,72 @@ export default function RealmRoleTabs() {
divider={false} divider={false}
/> />
<PageSection variant="light" className="pf-u-p-0"> <PageSection variant="light" className="pf-u-p-0">
<RoutableTabs isBox mountOnEnter defaultLocation={toTab("details")}> <FormProvider {...form}>
<Tab <RoutableTabs isBox mountOnEnter defaultLocation={toTab("details")}>
title={<TabTitleText>{t("details")}</TabTitleText>}
{...detailsTab}
>
<RoleForm
form={form}
onSubmit={onSubmit}
role={clientRoleMatch ? "manage-clients" : "manage-realm"}
cancelLink={
clientRoleMatch
? toClient({ realm: realmName, clientId, tab: "roles" })
: toRealmRoles({ realm: realmName })
}
editMode
/>
</Tab>
{composites && (
<Tab <Tab
data-testid="associatedRolesTab" title={<TabTitleText>{t("details")}</TabTitleText>}
title={<TabTitleText>{t("associatedRolesText")}</TabTitleText>} {...detailsTab}
{...associatedRolesTab}
> >
<RoleMapping <RoleForm
name={roleName!} onSubmit={onSubmit}
id={id} role={clientRoleMatch ? "manage-clients" : "manage-realm"}
type="roles" cancelLink={
isManager clientRoleMatch
save={(rows) => addComposites(rows.map((r) => r.role))} ? toClient({ realm: realmName, clientId, tab: "roles" })
/> : toRealmRoles({ realm: realmName })
</Tab>
)}
{!isDefaultRole(roleName) && (
<Tab
data-testid="attributesTab"
className="kc-attributes-tab"
title={<TabTitleText>{t("attributes")}</TabTitleText>}
{...attributesTab}
>
<AttributesForm
form={form}
save={onSubmit}
reset={() =>
setValue("attributes", attributes, { shouldDirty: false })
} }
editMode
/> />
</Tab> </Tab>
)} {composites && (
{!isDefaultRole(roleName) && ( <Tab
<Tab data-testid="associatedRolesTab"
title={<TabTitleText>{t("usersInRole")}</TabTitleText>} title={<TabTitleText>{t("associatedRolesText")}</TabTitleText>}
{...usersInRoleTab} {...associatedRolesTab}
> >
<UsersInRoleTab data-cy="users-in-role-tab" /> <RoleMapping
</Tab> name={roleName!}
)} id={id}
{isFeatureEnabled(Feature.AdminFineGrainedAuthz) && ( type="roles"
<Tab isManager
title={<TabTitleText>{t("permissions")}</TabTitleText>} save={(rows) => addComposites(rows.map((r) => r.role))}
{...permissionsTab} />
> </Tab>
<PermissionsTab id={id} type="roles" /> )}
</Tab> {!isDefaultRole(roleName) && (
)} <Tab
</RoutableTabs> data-testid="attributesTab"
className="kc-attributes-tab"
title={<TabTitleText>{t("attributes")}</TabTitleText>}
{...attributesTab}
>
<AttributesForm
form={form}
save={onSubmit}
reset={() =>
setValue("attributes", attributes, { shouldDirty: false })
}
/>
</Tab>
)}
{!isDefaultRole(roleName) && (
<Tab
title={<TabTitleText>{t("usersInRole")}</TabTitleText>}
{...usersInRoleTab}
>
<UsersInRoleTab data-cy="users-in-role-tab" />
</Tab>
)}
{isFeatureEnabled(Feature.AdminFineGrainedAuthz) && (
<Tab
title={<TabTitleText>{t("permissions")}</TabTitleText>}
{...permissionsTab}
>
<PermissionsTab id={id} type="roles" />
</Tab>
)}
</RoutableTabs>
</FormProvider>
</PageSection> </PageSection>
</> </>
); );