migrated some of the controls to ui-shared (#27424)

* migrated some of the controls to ui-shared

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/LdapSettingsGeneral.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/LdapSettingsGeneral.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/LdapSettingsKerberosIntegration.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/LdapSettingsSearching.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperDetails.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperDetails.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperDetails.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/mappers/LdapMapperDetails.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Update js/apps/admin-ui/src/user-federation/ldap/LdapSettingsGeneral.tsx

Co-authored-by: Jon Koops <jonkoops@gmail.com>
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* Apply suggestions from code review

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* review fixes

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* fixed test

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

* tests

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>

---------

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
Co-authored-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
Erik Jan de Wit 2024-03-06 09:40:54 +01:00 committed by GitHub
parent 61c3edcab8
commit b486972485
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 382 additions and 953 deletions

View file

@ -1,23 +1,23 @@
export default class ProviderPage { export default class ProviderPage {
// KerberosSettingsRequired input values // KerberosSettingsRequired input values
#kerberosNameInput = "kerberos-name"; #kerberosNameInput = "name";
#kerberosRealmInput = "kerberos-realm"; #kerberosRealmInput = "config.kerberosRealm.0";
#kerberosPrincipalInput = "kerberos-principal"; #kerberosPrincipalInput = "config.serverPrincipal.0";
#kerberosKeytabInput = "kerberos-keytab"; #kerberosKeytabInput = "config.keyTab.0";
// LdapSettingsGeneral input values // LdapSettingsGeneral input values
#ldapNameInput = "ldap-name"; #ldapNameInput = "name";
#ldapVendorInput = "#kc-vendor"; #ldapVendorInput = "#kc-vendor";
#ldapVendorList = "#kc-vendor + ul"; #ldapVendorList = "#kc-vendor + ul";
// LdapSettingsConnection input values // LdapSettingsConnection input values
connectionUrlInput = "ldap-connection-url"; connectionUrlInput = "config.connectionUrl.0";
truststoreSpiInput = "#kc-use-truststore-spi"; truststoreSpiInput = "#kc-use-truststore-spi";
truststoreSpiList = "#kc-use-truststore-spi + ul"; truststoreSpiList = "#kc-use-truststore-spi + ul";
connectionTimeoutInput = "connection-timeout"; connectionTimeoutInput = "config.connectionTimeout.0";
bindTypeInput = "#kc-bind-type"; bindTypeInput = "#kc-bind-type";
#bindTypeList = "#kc-bind-type + ul"; #bindTypeList = "#kc-bind-type + ul";
bindDnInput = "ldap-bind-dn"; bindDnInput = "config.bindDn.0";
bindCredsInput = "ldap-bind-credentials"; bindCredsInput = "ldap-bind-credentials";
#testConnectionBtn = "test-connection-button"; #testConnectionBtn = "test-connection-button";
#testAuthBtn = "test-auth-button"; #testAuthBtn = "test-auth-button";
@ -28,26 +28,26 @@ export default class ProviderPage {
ldapSearchScopeInput = "#kc-search-scope"; ldapSearchScopeInput = "#kc-search-scope";
#ldapSearchScopeInputList = "#kc-search-scope + ul"; #ldapSearchScopeInputList = "#kc-search-scope + ul";
ldapPagination = "ui-pagination"; ldapPagination = "ui-pagination";
ldapUsersDnInput = "ldap-users-dn"; ldapUsersDnInput = "config.usersDn.0";
ldapUserLdapAttInput = "ldap-username-attribute"; ldapUserLdapAttInput = "config.usernameLDAPAttribute.0";
ldapRdnLdapAttInput = "ldap-rdn-attribute"; ldapRdnLdapAttInput = "config.rdnLDAPAttribute.0";
ldapUuidLdapAttInput = "ldap-uuid-attribute"; ldapUuidLdapAttInput = "config.uuidLDAPAttribute.0";
ldapUserObjClassesInput = "ldap-user-object-classes"; ldapUserObjClassesInput = "config.userObjectClasses.0";
ldapUserLdapFilter = "user-ldap-filter"; ldapUserLdapFilter = "config.customUserSearchFilter.0";
ldapReadTimeout = "ldap-read-timeout"; ldapReadTimeout = "config.readTimeout.0";
// LdapSettingsKerberosIntegration input values // LdapSettingsKerberosIntegration input values
ldapKerberosRealmInput = "kerberos-realm"; ldapKerberosRealmInput = "config.kerberosRealm.0";
ldapServerPrincipalInput = "kerberos-principal"; ldapServerPrincipalInput = "config.serverPrincipal.0";
ldapKeyTabInput = "kerberos-keytab"; ldapKeyTabInput = "config.keyTab.0";
allowKerberosAuth = "allow-kerberos-auth"; allowKerberosAuth = "allow-kerberos-auth";
debug = "debug"; debug = "debug";
useKerberosForPwAuth = "use-kerberos-pw-auth"; useKerberosForPwAuth = "use-kerberos-pw-auth";
// LdapSettingsSynchronization input values // LdapSettingsSynchronization input values
ldapBatchSizeInput = "batch-size"; ldapBatchSizeInput = "config.batchSizeForSync.0";
ldapFullSyncPeriodInput = "full-sync-period"; ldapFullSyncPeriodInput = "config.fullSyncPeriod.0";
ldapUsersSyncPeriodInput = "changed-users-sync-period"; ldapUsersSyncPeriodInput = "config.changedSyncPeriod.0";
importUsers = "import-users"; importUsers = "import-users";
periodicFullSync = "periodic-full-sync"; periodicFullSync = "periodic-full-sync";
periodicUsersSync = "periodic-changed-users-sync"; periodicUsersSync = "periodic-changed-users-sync";
@ -327,7 +327,7 @@ export default class ProviderPage {
cy.get("#kc-providerId").click(); cy.get("#kc-providerId").click();
cy.get("button").contains(mapperType).click(); cy.get("button").contains(mapperType).click();
cy.findByTestId("ldap-mapper-name").clear().type(`${mapperType}-test`); cy.findByTestId("name").clear().type(`${mapperType}-test`);
switch (mapperType) { switch (mapperType) {
case this.#msadUserAcctMapper: case this.#msadUserAcctMapper:

View file

@ -1,18 +1,17 @@
import { FormGroup, Switch } from "@patternfly/react-core"; import { FormGroup, Switch } from "@patternfly/react-core";
import { Controller, useFormContext } from "react-hook-form"; import { Controller, FormProvider, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem, TextControl } from "ui-shared";
import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
export const SyncSettings = () => { export const SyncSettings = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { control, register, watch } = useFormContext(); const form = useFormContext();
const { control, watch } = form;
const watchPeriodicSync = watch("config.fullSyncPeriod", "-1"); const watchPeriodicSync = watch("config.fullSyncPeriod", "-1");
const watchChangedSync = watch("config.changedSyncPeriod", "-1"); const watchChangedSync = watch("config.changedSyncPeriod", "-1");
return ( return (
<> <FormProvider {...form}>
<FormGroup <FormGroup
label={t("periodicFullSync")} label={t("periodicFullSync")}
labelIcon={ labelIcon={
@ -44,26 +43,14 @@ export const SyncSettings = () => {
/> />
</FormGroup> </FormGroup>
{watchPeriodicSync !== "-1" && ( {watchPeriodicSync !== "-1" && (
<FormGroup <TextControl
hasNoPaddingTop name="config.fullSyncPeriod"
label={t("fullSyncPeriod")} label={t("fullSyncPeriod")}
labelIcon={ labelIcon={t("fullSyncPeriodHelp")}
<HelpItem type="number"
helpText={t("fullSyncPeriodHelp")} min={-1}
fieldLabelId="fullSyncPeriod" defaultValue="604800"
/> />
}
fieldId="kc-full-sync-period"
>
<KeycloakTextInput
type="number"
min={-1}
defaultValue="604800"
id="kc-full-sync-period"
data-testid="full-sync-period"
{...register("config.fullSyncPeriod")}
/>
</FormGroup>
)} )}
<FormGroup <FormGroup
label={t("periodicChangedUsersSync")} label={t("periodicChangedUsersSync")}
@ -96,27 +83,15 @@ export const SyncSettings = () => {
/> />
</FormGroup> </FormGroup>
{watchChangedSync !== "-1" && ( {watchChangedSync !== "-1" && (
<FormGroup <TextControl
name="config.changedSyncPeriod"
label={t("changedUsersSyncPeriod")} label={t("changedUsersSyncPeriod")}
labelIcon={ labelIcon={t("changedUsersSyncHelp")}
<HelpItem type="number"
helpText={t("changedUsersSyncHelp")} min={-1}
fieldLabelId="changedUsersSyncPeriod" defaultValue="86400"
/> />
}
fieldId="kc-changed-users-sync-period"
hasNoPaddingTop
>
<KeycloakTextInput
type="number"
min={-1}
defaultValue="86400"
id="kc-changed-users-sync-period"
data-testid="changed-users-sync-period"
{...register("config.changedSyncPeriod")}
/>
</FormGroup>
)} )}
</> </FormProvider>
); );
}; };

View file

@ -7,10 +7,14 @@ import {
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { useState } from "react"; import { useState } from "react";
import { Controller, UseFormReturn, useWatch } from "react-hook-form"; import {
Controller,
FormProvider,
UseFormReturn,
useWatch,
} from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem } from "ui-shared"; import { HelpItem, TextControl } from "ui-shared";
import { adminClient } from "../../admin-client"; import { adminClient } from "../../admin-client";
import { FormAccess } from "../../components/form/FormAccess"; import { FormAccess } from "../../components/form/FormAccess";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput"; import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
@ -47,7 +51,7 @@ export const KerberosSettingsRequired = ({
); );
return ( return (
<> <FormProvider {...form}>
{showSectionHeading && ( {showSectionHeading && (
<WizardSectionHeader <WizardSectionHeader
title={t("requiredSettings")} title={t("requiredSettings")}
@ -58,162 +62,54 @@ export const KerberosSettingsRequired = ({
{/* Required settings */} {/* Required settings */}
<FormAccess role="manage-realm" isHorizontal> <FormAccess role="manage-realm" isHorizontal>
<FormGroup {/* These hidden fields are required so data object written back matches data retrieved */}
<KeycloakTextInput
hidden
defaultValue="kerberos"
{...form.register("providerId")}
/>
<KeycloakTextInput
hidden
defaultValue="org.keycloak.storage.UserStorageProvider"
{...form.register("providerType")}
/>
<KeycloakTextInput
hidden
defaultValue={realm}
{...form.register("parentId")}
/>
<TextControl
name="name"
label={t("uiDisplayName")} label={t("uiDisplayName")}
labelIcon={ labelIcon={t("uiDisplayNameHelp")}
<HelpItem rules={{
helpText={t("uiDisplayNameHelp")} required: t("validateName"),
fieldLabelId="uiDisplayName" }}
/> />
} <TextControl
fieldId="kc-ui-display-name" name="config.kerberosRealm.0"
isRequired
validated={form.formState.errors.name ? "error" : "default"}
helperTextInvalid={(form.formState.errors.name as any)?.message}
>
{/* These hidden fields are required so data object written back matches data retrieved */}
<KeycloakTextInput
hidden
id="kc-ui-providerId"
defaultValue="kerberos"
{...form.register("providerId")}
/>
<KeycloakTextInput
hidden
id="kc-ui-providerType"
defaultValue="org.keycloak.storage.UserStorageProvider"
{...form.register("providerType")}
/>
<KeycloakTextInput
hidden
id="kc-ui-parentId"
defaultValue={realm}
{...form.register("parentId")}
/>
<KeycloakTextInput
isRequired
id="kc-ui-name"
data-testid="kerberos-name"
validated={form.formState.errors.name ? "error" : "default"}
aria-label={t("uiDisplayName")}
{...form.register("name", {
required: {
value: true,
message: t("validateName"),
},
})}
/>
</FormGroup>
<FormGroup
label={t("kerberosRealm")} label={t("kerberosRealm")}
labelIcon={ labelIcon={t("kerberosRealmHelp")}
<HelpItem rules={{
helpText={t("kerberosRealmHelp")} required: t("validateRealm"),
fieldLabelId="kc-kerberos-realm" }}
/> />
} <TextControl
fieldId="kc-kerberos-realm" name="config.serverPrincipal.0"
isRequired
validated={
(form.formState.errors.config as any)?.kerberosRealm?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.kerberosRealm?.[0].message
}
>
<KeycloakTextInput
isRequired
id="kc-kerberos-realm"
data-testid="kerberos-realm"
validated={
(form.formState.errors.config as any)?.kerberosRealm?.[0]
? "error"
: "default"
}
{...form.register("config.kerberosRealm.0", {
required: {
value: true,
message: t("validateRealm"),
},
})}
/>
</FormGroup>
<FormGroup
label={t("serverPrincipal")} label={t("serverPrincipal")}
labelIcon={ labelIcon={t("serverPrincipalHelp")}
<HelpItem rules={{
helpText={t("serverPrincipalHelp")} required: t("validateServerPrincipal"),
fieldLabelId="serverPrincipal" }}
/> />
} <TextControl
fieldId="kc-server-principal" name="config.keyTab.0"
isRequired
validated={
(form.formState.errors.config as any)?.serverPrincipal?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.serverPrincipal?.[0].message
}
>
<KeycloakTextInput
isRequired
id="kc-server-principal"
data-testid="kerberos-principal"
validated={
(form.formState.errors.config as any)?.serverPrincipal?.[0]
? "error"
: "default"
}
{...form.register("config.serverPrincipal.0", {
required: {
value: true,
message: t("validateServerPrincipal"),
},
})}
/>
</FormGroup>
<FormGroup
label={t("keyTab")} label={t("keyTab")}
labelIcon={ labelIcon={t("keyTabHelp")}
<HelpItem helpText={t("keyTabHelp")} fieldLabelId="keyTab" /> rules={{
} required: t("validateKeyTab"),
fieldId="kc-key-tab" }}
isRequired />
validated={
(form.formState.errors.config as any)?.keyTab?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.keyTab?.[0].message
}
>
<KeycloakTextInput
isRequired
id="kc-key-tab"
data-testid="kerberos-keytab"
validated={
(form.formState.errors.config as any)?.keyTab?.[0]
? "error"
: "default"
}
{...form.register("config.keyTab.0", {
required: {
value: true,
message: t("validateKeyTab"),
},
})}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("debug")} label={t("debug")}
labelIcon={ labelIcon={
@ -222,7 +118,6 @@ export const KerberosSettingsRequired = ({
fieldId="kc-debug" fieldId="kc-debug"
hasNoPaddingTop hasNoPaddingTop
> >
{" "}
<Controller <Controller
name="config.debug" name="config.debug"
defaultValue={["false"]} defaultValue={["false"]}
@ -240,7 +135,6 @@ export const KerberosSettingsRequired = ({
)} )}
/> />
</FormGroup> </FormGroup>
<FormGroup <FormGroup
label={t("allowPasswordAuthentication")} label={t("allowPasswordAuthentication")}
labelIcon={ labelIcon={
@ -269,7 +163,6 @@ export const KerberosSettingsRequired = ({
)} )}
/> />
</FormGroup> </FormGroup>
{isEqual(allowPassAuth, ["true"]) ? ( {isEqual(allowPassAuth, ["true"]) ? (
<FormGroup <FormGroup
label={t("editMode")} label={t("editMode")}
@ -282,7 +175,6 @@ export const KerberosSettingsRequired = ({
isRequired isRequired
fieldId="kc-edit-mode" fieldId="kc-edit-mode"
> >
{" "}
<Controller <Controller
name="config.editMode[0]" name="config.editMode[0]"
defaultValue="READ_ONLY" defaultValue="READ_ONLY"
@ -310,7 +202,6 @@ export const KerberosSettingsRequired = ({
></Controller> ></Controller>
</FormGroup> </FormGroup>
) : null} ) : null}
<FormGroup <FormGroup
label={t("updateFirstLogin")} label={t("updateFirstLogin")}
labelIcon={ labelIcon={
@ -340,6 +231,6 @@ export const KerberosSettingsRequired = ({
/> />
</FormGroup> </FormGroup>
</FormAccess> </FormAccess>
</> </FormProvider>
); );
}; };

View file

@ -11,14 +11,17 @@ import {
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { get, isEqual } from "lodash-es"; import { get, isEqual } from "lodash-es";
import { useState } from "react"; import { useState } from "react";
import { Controller, UseFormReturn, useWatch } from "react-hook-form"; import {
Controller,
FormProvider,
UseFormReturn,
useWatch,
} from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem } from "ui-shared"; import { HelpItem, TextControl } from "ui-shared";
import { adminClient } from "../../admin-client"; import { adminClient } from "../../admin-client";
import { useAlerts } from "../../components/alert/Alerts"; import { useAlerts } from "../../components/alert/Alerts";
import { FormAccess } from "../../components/form/FormAccess"; import { FormAccess } from "../../components/form/FormAccess";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { PasswordInput } from "../../components/password-input/PasswordInput"; import { PasswordInput } from "../../components/password-input/PasswordInput";
import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader";
import { useRealm } from "../../context/realm-context/RealmContext"; import { useRealm } from "../../context/realm-context/RealmContext";
@ -89,7 +92,7 @@ export const LdapSettingsConnection = ({
}); });
return ( return (
<> <FormProvider {...form}>
{showSectionHeading && ( {showSectionHeading && (
<WizardSectionHeader <WizardSectionHeader
title={t("connectionAndAuthenticationSettings")} title={t("connectionAndAuthenticationSettings")}
@ -98,43 +101,15 @@ export const LdapSettingsConnection = ({
/> />
)} )}
<FormAccess role="manage-realm" isHorizontal> <FormAccess role="manage-realm" isHorizontal>
<FormGroup <TextControl
name="config.connectionUrl.0"
label={t("connectionURL")} label={t("connectionURL")}
labelIcon={ labelIcon={t("consoleDisplayConnectionUrlHelp")}
<HelpItem type="url"
helpText={t("consoleDisplayConnectionUrlHelp")} rules={{
fieldLabelId="connectionURL" required: t("validateConnectionUrl"),
/> }}
} />
fieldId="kc-ui-connection-url"
isRequired
validated={
(form.formState.errors.config as any)?.connectionUrl?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.connectionUrl?.[0].message
}
>
<KeycloakTextInput
isRequired
type="url"
id="kc-ui-connection-url"
data-testid="ldap-connection-url"
validated={
(form.formState.errors.config as any)?.connectionUrl?.[0]
? "error"
: "default"
}
{...form.register("config.connectionUrl.0", {
required: {
value: true,
message: t("validateConnectionUrl").toString(),
},
})}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("enableStartTls")} label={t("enableStartTls")}
labelIcon={ labelIcon={
@ -162,9 +137,8 @@ export const LdapSettingsConnection = ({
aria-label={t("enableStartTls")} aria-label={t("enableStartTls")}
/> />
)} )}
></Controller> />
</FormGroup> </FormGroup>
<FormGroup <FormGroup
label={t("useTruststoreSpi")} label={t("useTruststoreSpi")}
labelIcon={ labelIcon={
@ -196,7 +170,7 @@ export const LdapSettingsConnection = ({
<SelectOption value="never">{t("never")}</SelectOption> <SelectOption value="never">{t("never")}</SelectOption>
</Select> </Select>
)} )}
></Controller> />
</FormGroup> </FormGroup>
<FormGroup <FormGroup
label={t("connectionPooling")} label={t("connectionPooling")}
@ -225,26 +199,15 @@ export const LdapSettingsConnection = ({
aria-label={t("connectionPooling")} aria-label={t("connectionPooling")}
/> />
)} )}
></Controller>
</FormGroup>
<FormGroup
label={t("connectionTimeout")}
labelIcon={
<HelpItem
helpText={t("connectionTimeoutHelp")}
fieldLabelId="consoleTimeout"
/>
}
fieldId="kc-ui-connection-timeout"
>
<KeycloakTextInput
type="number"
min={0}
id="kc-ui-connection-timeout"
data-testid="connection-timeout"
{...form.register("config.connectionTimeout.0")}
/> />
</FormGroup> </FormGroup>
<TextControl
name="config.connectionTimeout.0"
label={t("connectionTimeout")}
labelIcon={t("connectionTimeoutHelp")}
type="number"
min={0}
/>
<FormGroup fieldId="kc-test-connection-button"> <FormGroup fieldId="kc-test-connection-button">
<Button <Button
variant="secondary" variant="secondary"
@ -288,37 +251,19 @@ export const LdapSettingsConnection = ({
<SelectOption value="none" /> <SelectOption value="none" />
</Select> </Select>
)} )}
></Controller> />
</FormGroup> </FormGroup>
{isEqual(ldapBindType, ["simple"]) && ( {isEqual(ldapBindType, ["simple"]) && (
<> <>
<FormGroup <TextControl
name="config.bindDn.0"
label={t("bindDn")} label={t("bindDn")}
labelIcon={ labelIcon={t("bindDnHelp")}
<HelpItem helpText={t("bindDnHelp")} fieldLabelId="bindDn" /> rules={{
} required: t("validateBindDn"),
fieldId="kc-ui-bind-dn" }}
helperTextInvalid={t("validateBindDn")} />
validated={
(form.formState.errors.config as any)?.bindDn
? ValidatedOptions.error
: ValidatedOptions.default
}
isRequired
>
<KeycloakTextInput
type="text"
id="kc-ui-bind-dn"
data-testid="ldap-bind-dn"
validated={
(form.formState.errors.config as any)?.bindDn
? ValidatedOptions.error
: ValidatedOptions.default
}
{...form.register("config.bindDn.0", { required: true })}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("bindCredentials")} label={t("bindCredentials")}
labelIcon={ labelIcon={
@ -364,6 +309,6 @@ export const LdapSettingsConnection = ({
</Button> </Button>
</FormGroup> </FormGroup>
</FormAccess> </FormAccess>
</> </FormProvider>
); );
}; };

View file

@ -6,9 +6,9 @@ import {
SelectVariant, SelectVariant,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { useState } from "react"; import { useState } from "react";
import { Controller, UseFormReturn } from "react-hook-form"; import { Controller, FormProvider, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem } from "ui-shared"; import { HelpItem, TextControl } from "ui-shared";
import { adminClient } from "../../admin-client"; import { adminClient } from "../../admin-client";
import { FormAccess } from "../../components/form/FormAccess"; import { FormAccess } from "../../components/form/FormAccess";
@ -98,7 +98,7 @@ export const LdapSettingsGeneral = ({
}; };
return ( return (
<> <FormProvider {...form}>
{showSectionHeading && ( {showSectionHeading && (
<WizardSectionHeader <WizardSectionHeader
title={t("generalOptions")} title={t("generalOptions")}
@ -107,52 +107,31 @@ export const LdapSettingsGeneral = ({
/> />
)} )}
<FormAccess role="manage-realm" isHorizontal> <FormAccess role="manage-realm" isHorizontal>
<FormGroup {/* These hidden fields are required so data object written back matches data retrieved */}
<KeycloakTextInput
hidden
defaultValue="ldap"
{...form.register("providerId")}
/>
<KeycloakTextInput
hidden
defaultValue="org.keycloak.storage.UserStorageProvider"
{...form.register("providerType")}
/>
<KeycloakTextInput
hidden
defaultValue={realm}
{...form.register("parentId")}
/>
<TextControl
name="name"
label={t("uiDisplayName")} label={t("uiDisplayName")}
labelIcon={ labelIcon={t("uiDisplayNameHelp")}
<HelpItem defaultValue="ldap"
helpText={t("uiDisplayNameHelp")} rules={{
fieldLabelId="uiDisplayName" required: t("validateName"),
/> }}
} />
fieldId="kc-ui-display-name"
isRequired
validated={form.formState.errors.name ? "error" : "default"}
helperTextInvalid={form.formState.errors.name?.message}
>
{/* These hidden fields are required so data object written back matches data retrieved */}
<KeycloakTextInput
hidden
id="kc-ui-provider-id"
defaultValue="ldap"
{...form.register("providerId")}
/>
<KeycloakTextInput
hidden
id="kc-ui-provider-type"
defaultValue="org.keycloak.storage.UserStorageProvider"
{...form.register("providerType")}
/>
<KeycloakTextInput
hidden
id="kc-ui-parentId"
defaultValue={realm}
{...form.register("parentId")}
/>
<KeycloakTextInput
isRequired
id="kc-ui-display-name"
defaultValue="ldap"
data-testid="ldap-name"
validated={form.formState.errors.name ? "error" : "default"}
{...form.register("name", {
required: {
value: true,
message: `${t("validateName")}`,
},
})}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("vendor")} label={t("vendor")}
labelIcon={ labelIcon={
@ -201,6 +180,6 @@ export const LdapSettingsGeneral = ({
></Controller> ></Controller>
</FormGroup> </FormGroup>
</FormAccess> </FormAccess>
</> </FormProvider>
); );
}; };

View file

@ -1,10 +1,13 @@
import { FormGroup, Switch } from "@patternfly/react-core"; import { FormGroup, Switch } from "@patternfly/react-core";
import { Controller, UseFormReturn, useWatch } from "react-hook-form"; import {
Controller,
FormProvider,
UseFormReturn,
useWatch,
} from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem, TextControl } from "ui-shared";
import { FormAccess } from "../../components/form/FormAccess"; import { FormAccess } from "../../components/form/FormAccess";
import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader";
export type LdapSettingsKerberosIntegrationProps = { export type LdapSettingsKerberosIntegrationProps = {
@ -27,7 +30,7 @@ export const LdapSettingsKerberosIntegration = ({
}); });
return ( return (
<> <FormProvider {...form}>
{showSectionHeading && ( {showSectionHeading && (
<WizardSectionHeader <WizardSectionHeader
title={t("kerberosIntegration")} title={t("kerberosIntegration")}
@ -69,148 +72,35 @@ export const LdapSettingsKerberosIntegration = ({
{allowKerberosAuth[0] === "true" && ( {allowKerberosAuth[0] === "true" && (
<> <>
<FormGroup <TextControl
name="config.kerberosRealm.0"
label={t("kerberosRealm")} label={t("kerberosRealm")}
labelIcon={ labelIcon={t("kerberosRealmHelp")}
<HelpItem rules={{
helpText={t("kerberosRealmHelp")} required: t("validateRealm"),
fieldLabelId="kerberosRealm" }}
/> />
} <TextControl
fieldId="kc-kerberos-realm" name="config.serverPrincipal.0"
isRequired
validated={
(form.formState.errors.config as any)?.kerberosRealm?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.kerberosRealm?.[0]
.message
}
>
<KeycloakTextInput
isRequired
id="kc-kerberos-realm"
data-testid="kerberos-realm"
validated={
(form.formState.errors.config as any)?.kerberosRealm?.[0]
? "error"
: "default"
}
{...form.register("config.kerberosRealm.0", {
required: {
value: true,
message: t("validateRealm").toString(),
},
})}
/>
</FormGroup>
<FormGroup
label={t("serverPrincipal")} label={t("serverPrincipal")}
labelIcon={ labelIcon={t("serverPrincipalHelp")}
<HelpItem rules={{
helpText={t("serverPrincipalHelp")} required: t("validateServerPrincipal"),
fieldLabelId="serverPrincipal" }}
/> />
} <TextControl
fieldId="kc-server-principal" name="config.keyTab.0"
isRequired
validated={
(form.formState.errors.config as any)?.serverPrincipal?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.serverPrincipal?.[0]
.message
}
>
<KeycloakTextInput
isRequired
id="kc-server-principal"
data-testid="kerberos-principal"
validated={
(form.formState.errors.config as any)?.serverPrincipal?.[0]
? "error"
: "default"
}
{...form.register("config.serverPrincipal.0", {
required: {
value: true,
message: `${t("validateServerPrincipal")}`,
},
})}
/>
</FormGroup>
<FormGroup
label={t("keyTab")} label={t("keyTab")}
labelIcon={ labelIcon={t("keyTabHelp")}
<HelpItem helpText={t("keyTabHelp")} fieldLabelId="keyTab" /> rules={{
} required: t("validateKeyTab"),
fieldId="kc-key-tab" }}
isRequired />
validated={ <TextControl
(form.formState.errors.config as any)?.keyTab?.[0] name="config.krbPrincipalAttribute.0"
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.keyTab?.[0].message
}
>
<KeycloakTextInput
isRequired
id="kc-key-tab"
data-testid="kerberos-keytab"
validated={
(form.formState.errors.config as any)?.keyTab?.[0]
? "error"
: "default"
}
{...form.register("config.keyTab.0", {
required: {
value: true,
message: `${t("validateKeyTab")}`,
},
})}
/>
</FormGroup>
<FormGroup
label={t("krbPrincipalAttribute")} label={t("krbPrincipalAttribute")}
labelIcon={ labelIcon={t("krbPrincipalAttributeHelp")}
<HelpItem />
helpText={t("krbPrincipalAttributeHelp")}
fieldLabelId="krbPrincipalAttribute"
/>
}
fieldId="kc-krb-principal-attribute"
validated={
(form.formState.errors.config as any)
?.krbPrincipalAttribute?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)
?.krbPrincipalAttribute?.[0].message
}
>
<KeycloakTextInput
id="kc-krb-principal-attribute"
data-testid="krb-principal-attribute"
validated={
(form.formState.errors.config as any)
?.krbPrincipalAttribute?.[0]
? "error"
: "default"
}
{...form.register("config.krbPrincipalAttribute.0")}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("debug")} label={t("debug")}
@ -220,7 +110,6 @@ export const LdapSettingsKerberosIntegration = ({
fieldId="kc-debug" fieldId="kc-debug"
hasNoPaddingTop hasNoPaddingTop
> >
{" "}
<Controller <Controller
name="config.debug" name="config.debug"
defaultValue={["false"]} defaultValue={["false"]}
@ -237,7 +126,7 @@ export const LdapSettingsKerberosIntegration = ({
aria-label={t("debug")} aria-label={t("debug")}
/> />
)} )}
></Controller> />
</FormGroup> </FormGroup>
</> </>
)} )}
@ -271,6 +160,6 @@ export const LdapSettingsKerberosIntegration = ({
></Controller> ></Controller>
</FormGroup> </FormGroup>
</FormAccess> </FormAccess>
</> </FormProvider>
); );
}; };

View file

@ -6,12 +6,10 @@ import {
Switch, Switch,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { useState } from "react"; import { useState } from "react";
import { Controller, UseFormReturn } from "react-hook-form"; import { Controller, FormProvider, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem, TextControl } from "ui-shared";
import { FormAccess } from "../../components/form/FormAccess"; import { FormAccess } from "../../components/form/FormAccess";
import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader";
export type LdapSettingsSearchingProps = { export type LdapSettingsSearchingProps = {
@ -33,7 +31,7 @@ export const LdapSettingsSearching = ({
const [isReferralDropdownOpen, setIsReferralDropdownOpen] = useState(false); const [isReferralDropdownOpen, setIsReferralDropdownOpen] = useState(false);
return ( return (
<> <FormProvider {...form}>
{showSectionHeading && ( {showSectionHeading && (
<WizardSectionHeader <WizardSectionHeader
title={t("ldapSearchingAndUpdatingSettings")} title={t("ldapSearchingAndUpdatingSettings")}
@ -111,227 +109,61 @@ export const LdapSettingsSearching = ({
)} )}
/> />
</FormGroup> </FormGroup>
<FormGroup <TextControl
name="config.usersDn.0"
label={t("usersDN")} label={t("usersDN")}
labelIcon={ labelIcon={t("usersDNHelp")}
<HelpItem helpText={t("usersDNHelp")} fieldLabelId="usersDn" /> rules={{
} required: t("validateUsersDn"),
fieldId="kc-ui-users-dn" }}
isRequired />
validated={ <TextControl
(form.formState.errors.config as any)?.usersDn?.[0] name="config.usernameLDAPAttribute.0"
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.usersDn?.[0].message
}
>
<KeycloakTextInput
isRequired
defaultValue=""
id="kc-ui-users-dn"
data-testid="ldap-users-dn"
validated={
(form.formState.errors.config as any)?.usersDn?.[0]
? "error"
: "default"
}
{...form.register("config.usersDn.0", {
required: {
value: true,
message: t("validateUsersDn").toString(),
},
})}
/>
</FormGroup>
<FormGroup
label={t("usernameLdapAttribute")} label={t("usernameLdapAttribute")}
labelIcon={ labelIcon={t("usernameLdapAttributeHelp")}
<HelpItem defaultValue="cn"
helpText={t("usernameLdapAttributeHelp")} rules={{
fieldLabelId="usernameLdapAttribute" required: t("validateUsernameLDAPAttribute"),
/> }}
} />
fieldId="kc-username-ldap-attribute" <TextControl
isRequired name="config.rdnLDAPAttribute.0"
validated={
(form.formState.errors.config as any)?.usernameLDAPAttribute?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.usernameLDAPAttribute?.[0]
.message
}
>
<KeycloakTextInput
isRequired
defaultValue="cn"
id="kc-username-ldap-attribute"
data-testid="ldap-username-attribute"
validated={
(form.formState.errors.config as any)?.usernameLDAPAttribute?.[0]
? "error"
: "default"
}
{...form.register("config.usernameLDAPAttribute.0", {
required: {
value: true,
message: `${t("validateUsernameLDAPAttribute")}`,
},
})}
/>
</FormGroup>
<FormGroup
label={t("rdnLdapAttribute")} label={t("rdnLdapAttribute")}
labelIcon={ labelIcon={t("rdnLdapAttributeHelp")}
<HelpItem defaultValue="cn"
helpText={t("rdnLdapAttributeHelp")} rules={{
fieldLabelId="rdnLdapAttribute" required: t("validateRdnLdapAttribute"),
/> }}
} />
fieldId="kc-rdn-ldap-attribute" <TextControl
isRequired name="config.uuidLDAPAttribute.0"
validated={
(form.formState.errors.config as any)?.rdnLDAPAttribute?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.rdnLDAPAttribute?.[0].message
}
>
<KeycloakTextInput
isRequired
defaultValue="cn"
id="kc-rdn-ldap-attribute"
data-testid="ldap-rdn-attribute"
validated={
(form.formState.errors.config as any)?.rdnLDAPAttribute?.[0]
? "error"
: "default"
}
{...form.register("config.rdnLDAPAttribute.0", {
required: {
value: true,
message: `${t("validateRdnLdapAttribute")}`,
},
})}
/>
</FormGroup>
<FormGroup
label={t("uuidLdapAttribute")} label={t("uuidLdapAttribute")}
labelIcon={ labelIcon={t("uuidLdapAttributeHelp")}
<HelpItem defaultValue="objectGUID"
helpText={t("uuidLdapAttributeHelp")} rules={{
fieldLabelId="uuidLdapAttribute" required: t("validateUuidLDAPAttribute"),
/> }}
} />
fieldId="kc-uuid-ldap-attribute" <TextControl
isRequired name="config.userObjectClasses.0"
validated={
(form.formState.errors.config as any)?.uuidLDAPAttribute?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.uuidLDAPAttribute?.[0]
.message
}
>
<KeycloakTextInput
isRequired
defaultValue="objectGUID"
id="kc-uuid-ldap-attribute"
data-testid="ldap-uuid-attribute"
validated={
(form.formState.errors.config as any)?.uuidLDAPAttribute?.[0]
? "error"
: "default"
}
{...form.register("config.uuidLDAPAttribute.0", {
required: {
value: true,
message: `${t("validateUuidLDAPAttribute")}`,
},
})}
/>
</FormGroup>
<FormGroup
label={t("userObjectClasses")} label={t("userObjectClasses")}
labelIcon={ labelIcon={t("userObjectClassesHelp")}
<HelpItem defaultValue="person, organizationalPerson, user"
helpText={t("userObjectClassesHelp")} rules={{
fieldLabelId="userObjectClasses" required: t("validateUserObjectClasses"),
/> }}
} />
fieldId="kc-user-object-classes" <TextControl
isRequired name="config.customUserSearchFilter.0"
validated={
(form.formState.errors.config as any)?.userObjectClasses?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.userObjectClasses?.[0]
.message
}
>
<KeycloakTextInput
isRequired
defaultValue="person, organizationalPerson, user"
id="kc-user-object-classes"
data-testid="ldap-user-object-classes"
validated={
(form.formState.errors.config as any)?.userObjectClasses?.[0]
? "error"
: "default"
}
{...form.register("config.userObjectClasses.0", {
required: {
value: true,
message: t("validateUserObjectClasses").toString(),
},
})}
/>
</FormGroup>
<FormGroup
label={t("userLdapFilter")} label={t("userLdapFilter")}
labelIcon={ labelIcon={t("userLdapFilterHelp")}
<HelpItem rules={{
helpText={t("userLdapFilterHelp")} pattern: {
fieldLabelId="userLdapFilter" value: /(\(.*\))$/,
/> message: t("validateCustomUserSearchFilter"),
} },
fieldId="kc-user-ldap-filter" }}
validated={ />
(form.formState.errors.config as any)?.customUserSearchFilter?.[0]
? "error"
: "default"
}
helperTextInvalid={
(form.formState.errors.config as any)?.customUserSearchFilter?.[0]
.message
}
>
<KeycloakTextInput
id="kc-user-ldap-filter"
data-testid="user-ldap-filter"
validated={
(form.formState.errors.config as any)?.customUserSearchFilter?.[0]
? "error"
: "default"
}
{...form.register("config.customUserSearchFilter.0", {
pattern: {
value: /(\(.*\))$/,
message: t("validateCustomUserSearchFilter").toString(),
},
})}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("searchScope")} label={t("searchScope")}
labelIcon={ labelIcon={
@ -369,26 +201,15 @@ export const LdapSettingsSearching = ({
</SelectOption> </SelectOption>
</Select> </Select>
)} )}
></Controller>
</FormGroup>
<FormGroup
label={t("readTimeout")}
labelIcon={
<HelpItem
helpText={t("readTimeoutHelp")}
fieldLabelId="readTimeout"
/>
}
fieldId="kc-read-timeout"
>
<KeycloakTextInput
type="number"
min={0}
id="kc-read-timeout"
data-testid="ldap-read-timeout"
{...form.register("config.readTimeout.0")}
/> />
</FormGroup> </FormGroup>
<TextControl
name="config.readTimeout.0"
label={t("readTimeout")}
labelIcon={t("readTimeoutHelp")}
type="number"
min={0}
/>
<FormGroup <FormGroup
label={t("pagination")} label={t("pagination")}
labelIcon={ labelIcon={
@ -450,6 +271,6 @@ export const LdapSettingsSearching = ({
></Controller> ></Controller>
</FormGroup> </FormGroup>
</FormAccess> </FormAccess>
</> </FormProvider>
); );
}; };

View file

@ -1,10 +1,8 @@
import { FormGroup, Switch } from "@patternfly/react-core"; import { FormGroup, Switch } from "@patternfly/react-core";
import { Controller, UseFormReturn } from "react-hook-form"; import { Controller, FormProvider, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem, TextControl } from "ui-shared";
import { FormAccess } from "../../components/form/FormAccess"; import { FormAccess } from "../../components/form/FormAccess";
import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader"; import { WizardSectionHeader } from "../../components/wizard-section-header/WizardSectionHeader";
export type LdapSettingsSynchronizationProps = { export type LdapSettingsSynchronizationProps = {
@ -24,7 +22,7 @@ export const LdapSettingsSynchronization = ({
const watchChangedSync = form.watch("config.periodicChangedUsersSync", false); const watchChangedSync = form.watch("config.periodicChangedUsersSync", false);
return ( return (
<> <FormProvider {...form}>
{showSectionHeading && ( {showSectionHeading && (
<WizardSectionHeader <WizardSectionHeader
title={t("synchronizationSettings")} title={t("synchronizationSettings")}
@ -32,6 +30,7 @@ export const LdapSettingsSynchronization = ({
showDescription={showSectionDescription} showDescription={showSectionDescription}
/> />
)} )}
<FormAccess role="manage-realm" isHorizontal> <FormAccess role="manage-realm" isHorizontal>
<FormGroup <FormGroup
hasNoPaddingTop hasNoPaddingTop
@ -91,21 +90,13 @@ export const LdapSettingsSynchronization = ({
)} )}
/> />
</FormGroup> </FormGroup>
<FormGroup <TextControl
name="config.batchSizeForSync.0"
type="number"
min={0}
label={t("batchSize")} label={t("batchSize")}
labelIcon={ labelIcon={t("batchSizeHelp")}
<HelpItem helpText={t("batchSizeHelp")} fieldLabelId="batchSize" /> />
}
fieldId="kc-batch-size"
>
<KeycloakTextInput
type="number"
min={0}
id="kc-batch-size"
data-testid="batch-size"
{...form.register("config.batchSizeForSync.0")}
/>
</FormGroup>
<FormGroup <FormGroup
label={t("periodicFullSync")} label={t("periodicFullSync")}
labelIcon={ labelIcon={
@ -133,29 +124,17 @@ export const LdapSettingsSynchronization = ({
aria-label={t("periodicFullSync")} aria-label={t("periodicFullSync")}
/> />
)} )}
></Controller> />
</FormGroup> </FormGroup>
{watchPeriodicSync && ( {watchPeriodicSync && (
<FormGroup <TextControl
hasNoPaddingTop name="config.fullSyncPeriod.0"
label={t("fullSyncPeriod")} label={t("fullSyncPeriod")}
labelIcon={ labelIcon={t("fullSyncPeriodHelp")}
<HelpItem type="number"
helpText={t("fullSyncPeriodHelp")} min={-1}
fieldLabelId="fullSyncPeriod" defaultValue={604800}
/> />
}
fieldId="kc-full-sync-period"
>
<KeycloakTextInput
type="number"
min={-1}
defaultValue={604800}
id="kc-full-sync-period"
data-testid="full-sync-period"
{...form.register("config.fullSyncPeriod.0")}
/>
</FormGroup>
)} )}
<FormGroup <FormGroup
label={t("periodicChangedUsersSync")} label={t("periodicChangedUsersSync")}
@ -184,31 +163,19 @@ export const LdapSettingsSynchronization = ({
aria-label={t("periodicChangedUsersSync")} aria-label={t("periodicChangedUsersSync")}
/> />
)} )}
></Controller> />
</FormGroup> </FormGroup>
{watchChangedSync && ( {watchChangedSync && (
<FormGroup <TextControl
name="config.changedSyncPeriod.0"
label={t("changedUsersSyncPeriod")} label={t("changedUsersSyncPeriod")}
labelIcon={ labelIcon={t("changedUsersSyncHelp")}
<HelpItem type="number"
helpText={t("changedUsersSyncHelp")} min={-1}
fieldLabelId="changedUsersSyncPeriod" defaultValue={86400}
/> />
}
fieldId="kc-changed-users-sync-period"
hasNoPaddingTop
>
<KeycloakTextInput
type="number"
min={-1}
defaultValue={86400}
id="kc-changed-users-sync-period"
data-testid="changed-users-sync-period"
{...form.register("config.changedSyncPeriod.0")}
/>
</FormGroup>
)} )}
</FormAccess> </FormAccess>
</> </FormProvider>
); );
}; };

View file

@ -7,20 +7,17 @@ import {
Button, Button,
ButtonVariant, ButtonVariant,
DropdownItem, DropdownItem,
Form,
FormGroup, FormGroup,
PageSection, PageSection,
Select, Select,
SelectOption, SelectOption,
SelectVariant, SelectVariant,
ValidatedOptions,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { useState } from "react"; import { useState } from "react";
import { Controller, FormProvider, useForm, useWatch } from "react-hook-form"; import { Controller, FormProvider, useForm, useWatch } 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";
import { HelpItem } from "ui-shared"; import { HelpItem, TextControl } from "ui-shared";
import { adminClient } from "../../../admin-client"; import { adminClient } from "../../../admin-client";
import { useAlerts } from "../../../components/alert/Alerts"; import { useAlerts } from "../../../components/alert/Alerts";
import { useConfirmDialog } from "../../../components/confirm-dialog/ConfirmDialog"; import { useConfirmDialog } from "../../../components/confirm-dialog/ConfirmDialog";
@ -208,163 +205,128 @@ export default function LdapMapperDetails() {
} }
/> />
<PageSection variant="light" isFilled> <PageSection variant="light" isFilled>
<FormAccess role="manage-realm" isHorizontal> <FormProvider {...form}>
{!isNew && ( <FormAccess
<FormGroup label={t("id")} fieldId="kc-ldap-mapper-id"> role="manage-realm"
<KeycloakTextInput isHorizontal
isDisabled onSubmit={form.handleSubmit(() => save(form.getValues()))}
id="kc-ldap-mapper-id"
data-testid="ldap-mapper-id"
{...form.register("id")}
/>
</FormGroup>
)}
<FormGroup
label={t("name")}
labelIcon={
<HelpItem helpText={t("nameHelp")} fieldLabelId="name" />
}
fieldId="kc-ldap-mapper-name"
isRequired
> >
<KeycloakTextInput {!isNew && <TextControl name="id" label={t("id")} isDisabled />}
<TextControl
name="name"
label={t("name")}
labelIcon={t("nameHelp")}
isDisabled={!isNew} isDisabled={!isNew}
isRequired rules={{ required: t("required") }}
id="kc-ldap-mapper-name"
data-testid="ldap-mapper-name"
validated={
form.formState.errors.name
? ValidatedOptions.error
: ValidatedOptions.default
}
{...form.register("name", { required: true })}
/> />
<KeycloakTextInput <KeycloakTextInput
hidden hidden
defaultValue={isNew ? id : mapping ? mapping.parentId : ""} defaultValue={isNew ? id : mapping ? mapping.parentId : ""}
id="kc-ldap-parentId"
data-testid="ldap-mapper-parentId" data-testid="ldap-mapper-parentId"
{...form.register("parentId")} {...form.register("parentId")}
/> />
<KeycloakTextInput <KeycloakTextInput
hidden hidden
defaultValue="org.keycloak.storage.ldap.mappers.LDAPStorageMapper" defaultValue="org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
id="kc-ldap-provider-type"
data-testid="ldap-mapper-provider-type" data-testid="ldap-mapper-provider-type"
{...form.register("providerType")} {...form.register("providerType")}
/> />
</FormGroup> {!isNew ? (
{!isNew ? ( <TextControl
<FormGroup
label={t("mapperType")}
labelIcon={
<HelpItem
helpText={
mapper?.helpText ? mapper.helpText : t("mapperTypeHelp")
}
fieldLabelId="mapperType"
/>
}
fieldId="kc-ldap-mapper-type"
isRequired
>
<KeycloakTextInput
isDisabled={!isNew}
isRequired
id="kc-ldap-mapper-type"
data-testid="ldap-mapper-type-fld"
{...form.register("providerId")}
/>
</FormGroup>
) : (
<FormGroup
label={t("mapperType")}
labelIcon={
<HelpItem
helpText={
mapper?.helpText ? mapper.helpText : t("mapperTypeHelp")
}
fieldLabelId="mapperType"
/>
}
fieldId="kc-providerId"
isRequired
>
<Controller
name="providerId" name="providerId"
defaultValue="" label={t("mapperType")}
control={form.control} labelIcon={
data-testid="ldap-mapper-type-select" mapper?.helpText ? mapper.helpText : t("mapperTypeHelp")
render={({ field }) => ( }
<Select rules={{ required: t("required") }}
toggleId="kc-providerId" isDisabled={!isNew}
typeAheadAriaLabel={t("mapperType")} />
required ) : (
onToggle={() => <FormGroup
setIsMapperDropdownOpen(!isMapperDropdownOpen) label={t("mapperType")}
labelIcon={
<HelpItem
helpText={
mapper?.helpText ? mapper.helpText : t("mapperTypeHelp")
} }
isOpen={isMapperDropdownOpen} fieldLabelId="mapperType"
onSelect={(_, value) => { />
setupForm({ }
providerId: value as string, fieldId="kc-providerId"
...Object.fromEntries( isRequired
components >
.find((c) => c.id === value) <Controller
?.properties.filter((m) => m.type === "List") name="providerId"
.map((m) => [ defaultValue=""
convertToName(m.name!), control={form.control}
m.options?.[0], data-testid="ldap-mapper-type-select"
]) || [], render={({ field }) => (
), <Select
}); toggleId="kc-providerId"
setIsMapperDropdownOpen(false); typeAheadAriaLabel={t("mapperType")}
}} required
selections={field.value} onToggle={() =>
variant={SelectVariant.typeahead} setIsMapperDropdownOpen(!isMapperDropdownOpen)
aria-label={t("selectMapperType")} }
> isOpen={isMapperDropdownOpen}
{components.map((c) => ( onSelect={(_, value) => {
<SelectOption key={c.id} value={c.id} /> setupForm({
))} providerId: value as string,
</Select> ...Object.fromEntries(
)} components
></Controller> .find((c) => c.id === value)
</FormGroup> ?.properties.filter((m) => m.type === "List")
)} .map((m) => [
<FormProvider {...form}> convertToName(m.name!),
m.options?.[0],
]) || [],
),
});
setIsMapperDropdownOpen(false);
}}
selections={field.value}
variant={SelectVariant.typeahead}
aria-label={t("selectMapperType")}
>
{components.map((c) => (
<SelectOption key={c.id} value={c.id} />
))}
</Select>
)}
></Controller>
</FormGroup>
)}
{!!mapperType && ( {!!mapperType && (
<DynamicComponents properties={mapper?.properties!} /> <DynamicComponents properties={mapper?.properties!} />
)} )}
</FormProvider> <ActionGroup>
</FormAccess> <Button
isDisabled={!form.formState.isDirty}
<Form onSubmit={form.handleSubmit(() => save(form.getValues()))}> variant="primary"
<ActionGroup> type="submit"
<Button data-testid="ldap-mapper-save"
isDisabled={!form.formState.isDirty} >
variant="primary" {t("save")}
type="submit" </Button>
data-testid="ldap-mapper-save" <Button
> variant="link"
{t("save")} onClick={() =>
</Button> isNew
<Button ? navigate(-1)
variant="link" : navigate(
onClick={() => `/${realm}/user-federation/ldap/${
isNew mapping!.parentId
? navigate(-1) }/mappers`,
: navigate( )
`/${realm}/user-federation/ldap/${ }
mapping!.parentId data-testid="ldap-mapper-cancel"
}/mappers`, >
) {t("cancel")}
} </Button>
data-testid="ldap-mapper-cancel" </ActionGroup>
> </FormAccess>
{t("cancel")} </FormProvider>
</Button>
</ActionGroup>
</Form>
</PageSection> </PageSection>
</> </>
); );