convert to ui-shared (#27708)

* convert to ui-shared

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

* review comments

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

* added default value for variant

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

* remove class

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

* spread rest of the properties

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

---------

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Erik Jan de Wit 2024-03-12 11:50:34 +01:00 committed by GitHub
parent d2a7e87967
commit f2cc4cbce0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 123 additions and 259 deletions

View file

@ -5,7 +5,7 @@ export default class CreateProviderPage {
#clientSecretField = "clientSecret"; #clientSecretField = "clientSecret";
#displayName = "displayName"; #displayName = "displayName";
#discoveryEndpoint = "discoveryEndpoint"; #discoveryEndpoint = "discoveryEndpoint";
#authorizationUrl = "authorizationUrl"; #authorizationUrl = "config.authorizationUrl";
#addButton = "createProvider"; #addButton = "createProvider";
#saveButton = "idp-details-save"; #saveButton = "idp-details-save";
#ssoServiceUrl = "sso-service-url"; #ssoServiceUrl = "sso-service-url";

View file

@ -79,10 +79,10 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
#saveBtn = "idp-details-save"; #saveBtn = "idp-details-save";
#revertBtn = "idp-details-revert"; #revertBtn = "idp-details-revert";
#validateSignature = "#validateSignature"; #validateSignature = "#config\\.validateSignature";
#jwksSwitch = "#useJwksUrl"; #jwksSwitch = "#config\\.useJwksUrl";
#jwksUrl = "jwksUrl"; #jwksUrl = "config.jwksUrl";
#pkceSwitch = "#pkceEnabled"; #pkceSwitch = "#config\\.pkceEnabled";
#pkceMethod = "#pkceMethod"; #pkceMethod = "#pkceMethod";
#clientAuth = "#clientAuthentication"; #clientAuth = "#clientAuthentication";
#clientAssertionSigningAlg = "#clientAssertionSigningAlg"; #clientAssertionSigningAlg = "#clientAssertionSigningAlg";
@ -347,9 +347,8 @@ export default class ProviderBaseGeneralSettingsPage extends PageObject {
public assertOIDCUrl(url: string) { public assertOIDCUrl(url: string) {
cy.findByTestId("jump-link-openid-connect-settings").click(); cy.findByTestId("jump-link-openid-connect-settings").click();
cy.findByTestId(url + "Url") cy.findByTestId(`config.${url}Url`).clear();
.clear() cy.findByTestId(`config.${url}Url`).type("invalidUrl");
.type("invalidUrl");
this.clickSaveBtn(); this.clickSaveBtn();
masthead.checkNotificationMessage( masthead.checkNotificationMessage(
"Could not update the provider The url [" + url + "_url] is malformed", "Could not update the provider The url [" + url + "_url] is malformed",

View file

@ -1,22 +1,10 @@
import IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation"; import IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
import { import { ExpandableSection } from "@patternfly/react-core";
ExpandableSection,
FormGroup,
Select,
SelectOption,
SelectVariant,
ValidatedOptions,
} from "@patternfly/react-core";
import { useState } from "react"; import { useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form"; import { useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { SelectControl, TextAreaControl, TextControl } from "ui-shared";
import { HelpItem } from "ui-shared"; import { DefaultSwitchControl } from "../../components/SwitchControl";
import { KeycloakTextArea } from "../../components/keycloak-text-area/KeycloakTextArea";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { FormGroupField } from "../component/FormGroupField";
import { SwitchField } from "../component/SwitchField";
import { TextField } from "../component/TextField";
import "./discovery-settings.css"; import "./discovery-settings.css";
@ -28,12 +16,7 @@ type DiscoverySettingsProps = {
const Fields = ({ readOnly }: DiscoverySettingsProps) => { const Fields = ({ readOnly }: DiscoverySettingsProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [pkceMethodOpen, setPkceMethodOpen] = useState(false); const { control } = useFormContext<IdentityProviderRepresentation>();
const {
register,
control,
formState: { errors },
} = useFormContext<IdentityProviderRepresentation>();
const validateSignature = useWatch({ const validateSignature = useWatch({
control, control,
@ -50,152 +33,93 @@ const Fields = ({ readOnly }: DiscoverySettingsProps) => {
return ( return (
<div className="pf-c-form pf-m-horizontal"> <div className="pf-c-form pf-m-horizontal">
<FormGroup <TextControl
name="config.authorizationUrl"
label={t("authorizationUrl")} label={t("authorizationUrl")}
fieldId="kc-authorization-url"
isRequired
validated={
errors.config?.authorizationUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
type="url" type="url"
data-testid="authorizationUrl" readOnly={readOnly}
id="kc-authorization-url" rules={{
validated={ required: t("required"),
errors.config?.authorizationUrl }}
? ValidatedOptions.error
: ValidatedOptions.default
}
isReadOnly={readOnly}
{...register("config.authorizationUrl", { required: true })}
/> />
</FormGroup> <TextControl
name="config.tokenUrl"
<FormGroup
label={t("tokenUrl")} label={t("tokenUrl")}
fieldId="tokenUrl"
isRequired
validated={
errors.config?.tokenUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
type="url" type="url"
id="tokenUrl" readOnly={readOnly}
data-testid="tokenUrl" rules={{
validated={ required: t("required"),
errors.config?.tokenUrl }}
? ValidatedOptions.error
: ValidatedOptions.default
}
isReadOnly={readOnly}
{...register("config.tokenUrl", { required: true })}
/> />
</FormGroup> <TextControl
<TextField name="config.logoutUrl"
field="config.logoutUrl" label={t("logoutUrl")}
label="logoutUrl" readOnly={readOnly}
isReadOnly={readOnly}
/> />
<TextField <TextControl
field="config.userInfoUrl" name="config.userInfoUrl"
label="userInfoUrl" label={t("userInfoUrl")}
isReadOnly={readOnly} readOnly={readOnly}
/> />
<TextField field="config.issuer" label="issuer" isReadOnly={readOnly} /> <TextControl
<SwitchField name="config.issuer"
field="config.validateSignature" label={t("issuer")}
label="validateSignature" readOnly={readOnly}
isReadOnly={readOnly} />
<DefaultSwitchControl
name="config.validateSignature"
label={t("validateSignature")}
isDisabled={readOnly}
stringify
/> />
{validateSignature === "true" && ( {validateSignature === "true" && (
<> <>
<SwitchField <DefaultSwitchControl
field="config.useJwksUrl" name="config.useJwksUrl"
label="useJwksUrl" label={t("useJwksUrl")}
data-testid="useJwksUrl" isDisabled={readOnly}
isReadOnly={readOnly} stringify
/> />
{useJwks === "true" ? ( {useJwks === "true" ? (
<TextField <TextAreaControl
field="config.jwksUrl" name="config.jwksUrl"
label="jwksUrl" label={t("jwksUrl")}
isReadOnly={readOnly} readOnly={readOnly}
/> />
) : ( ) : (
<> <>
<FormGroupField label="validatingPublicKey"> <TextControl
<KeycloakTextArea name="config.publicKeySignatureVerifier"
data-testid="validatingPublicKey" label="validatingPublicKey"
aria-label={t("validatingPublicKey")}
{...register("config.publicKeySignatureVerifier")}
/> />
</FormGroupField> <TextControl
<TextField name="config.publicKeySignatureVerifierKeyId"
field="config.publicKeySignatureVerifierKeyId" label={t("validatingPublicKeyId")}
label="validatingPublicKeyId" readOnly={readOnly}
isReadOnly={readOnly}
/> />
</> </>
)} )}
</> </>
)} )}
<SwitchField <DefaultSwitchControl
field="config.pkceEnabled" name="config.pkceEnabled"
label="pkceEnabled" label={t("pkceEnabled")}
isReadOnly={readOnly} isDisabled={readOnly}
stringify
/> />
{isPkceEnabled === "true" && ( {isPkceEnabled === "true" && (
<FormGroup <SelectControl
className="pf-u-pb-3xl"
label={t("pkceMethod")}
labelIcon={
<HelpItem
helpText={t("pkceMethodHelp")}
fieldLabelId="pkceMethod"
/>
}
fieldId="pkceMethod"
>
<Controller
name="config.pkceMethod" name="config.pkceMethod"
defaultValue={PKCE_METHODS[0]} label={t("pkceMethod")}
control={control} labelIcon={t("pkceMethodHelp")}
render={({ field }) => ( controller={{
<Select defaultValue: PKCE_METHODS[0],
toggleId="pkceMethod"
required
direction="down"
onToggle={() => setPkceMethodOpen(!pkceMethodOpen)}
onSelect={(_, value) => {
field.onChange(value as string);
setPkceMethodOpen(false);
}} }}
selections={t(`${field.value}`)} options={PKCE_METHODS.map((option) => ({
variant={SelectVariant.single} key: option,
aria-label={t("pkceMethod")} value: t(`${option}`),
isOpen={pkceMethodOpen} }))}
>
{PKCE_METHODS.map((option) => (
<SelectOption
selected={option === field.value}
key={option}
value={option}
>
{t(`${option}`)}
</SelectOption>
))}
</Select>
)}
/> />
</FormGroup>
)} )}
</div> </div>
); );

View file

@ -1,54 +1,33 @@
import { FormGroup, ValidatedOptions } from "@patternfly/react-core"; import { useFormContext, useWatch } from "react-hook-form";
import { useWatch, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { TextControl } from "ui-shared";
import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { DisplayOrder } from "../component/DisplayOrder"; import { DisplayOrder } from "../component/DisplayOrder";
import { RedirectUrl } from "../component/RedirectUrl"; import { RedirectUrl } from "../component/RedirectUrl";
import { TextField } from "../component/TextField";
import type { IdentityProviderParams } from "../routes/IdentityProvider"; import type { IdentityProviderParams } from "../routes/IdentityProvider";
export const OIDCGeneralSettings = () => { export const OIDCGeneralSettings = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { tab } = useParams<IdentityProviderParams>(); const { tab } = useParams<IdentityProviderParams>();
const { const { control } = useFormContext();
register,
control,
formState: { errors },
} = useFormContext();
const alias = useWatch({ control, name: "alias" }); const alias = useWatch({ control, name: "alias" });
return ( return (
<> <>
<RedirectUrl id={alias} /> <RedirectUrl id={alias} />
<FormGroup <TextControl
name="alias"
label={t("alias")} label={t("alias")}
labelIcon={<HelpItem helpText={t("aliasHelp")} fieldLabelId="alias" />} labelIcon={t("aliasHelp")}
fieldId="alias" readOnly={tab === "settings"}
isRequired rules={{
validated={ required: t("required"),
errors.alias ? ValidatedOptions.error : ValidatedOptions.default }}
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
isReadOnly={tab === "settings"}
isRequired
id="alias"
data-testid="alias"
validated={
errors.alias ? ValidatedOptions.error : ValidatedOptions.default
}
{...register("alias", { required: true })}
/> />
</FormGroup>
<TextField field="displayName" label="displayName" /> <TextControl name="displayName" label={t("displayName")} />
<DisplayOrder /> <DisplayOrder />
</> </>
); );

View file

@ -3,11 +3,9 @@ import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client
import { FormGroup, Title } from "@patternfly/react-core"; import { FormGroup, Title } from "@patternfly/react-core";
import { useFormContext } from "react-hook-form"; import { useFormContext } 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 { FileUploadForm } from "../../components/json-file-upload/FileUploadForm"; import { FileUploadForm } from "../../components/json-file-upload/FileUploadForm";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { useRealm } from "../../context/realm-context/RealmContext"; import { useRealm } from "../../context/realm-context/RealmContext";
import environment from "../../environment"; import environment from "../../environment";
import { addTrailingSlash } from "../../util"; import { addTrailingSlash } from "../../util";
@ -26,7 +24,6 @@ export const SamlConnectSettings = () => {
const { realm } = useRealm(); const { realm } = useRealm();
const { const {
setValue, setValue,
register,
setError, setError,
clearErrors, clearErrors,
formState: { errors }, formState: { errors },
@ -81,27 +78,15 @@ export const SamlConnectSettings = () => {
{t("samlSettings")} {t("samlSettings")}
</Title> </Title>
<FormGroup <TextControl
name="config.entityId"
label={t("serviceProviderEntityId")} label={t("serviceProviderEntityId")}
fieldId="kc-service-provider-entity-id" labelIcon={t("serviceProviderEntityIdHelp")}
labelIcon={
<HelpItem
helpText={t("serviceProviderEntityIdHelp")}
fieldLabelId="serviceProviderEntityId"
/>
}
isRequired
helperTextInvalid={t("required")}
validated={errors.config?.entityId ? "error" : "default"}
>
<KeycloakTextInput
data-testid="serviceProviderEntityId"
id="kc-service-provider-entity-id"
validated={errors.config?.entityId ? "error" : "default"}
defaultValue={`${environment.authServerUrl}/realms/${realm}`} defaultValue={`${environment.authServerUrl}/realms/${realm}`}
{...register("config.entityId", { required: true })} rules={{
required: t("required"),
}}
/> />
</FormGroup>
<DiscoveryEndpointField <DiscoveryEndpointField
id="saml" id="saml"

View file

@ -1,15 +1,12 @@
import { FormGroup, ValidatedOptions } from "@patternfly/react-core"; import { FormGroup } from "@patternfly/react-core";
import { useWatch, useFormContext } from "react-hook-form"; import { useFormContext, useWatch } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { HelpItem, TextControl } from "ui-shared";
import { FormattedLink } from "../../components/external-link/FormattedLink"; import { FormattedLink } from "../../components/external-link/FormattedLink";
import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";
import { useRealm } from "../../context/realm-context/RealmContext"; import { useRealm } from "../../context/realm-context/RealmContext";
import environment from "../../environment"; import environment from "../../environment";
import { DisplayOrder } from "../component/DisplayOrder"; import { DisplayOrder } from "../component/DisplayOrder";
import { RedirectUrl } from "../component/RedirectUrl"; import { RedirectUrl } from "../component/RedirectUrl";
import { TextField } from "../component/TextField";
import "./saml-general-settings.css"; import "./saml-general-settings.css";
@ -23,47 +20,26 @@ export const SamlGeneralSettings = ({
const { t } = useTranslation(); const { t } = useTranslation();
const { realm } = useRealm(); const { realm } = useRealm();
const { const { control } = useFormContext();
register,
control,
formState: { errors },
} = useFormContext();
const alias = useWatch({ control, name: "alias" }); const alias = useWatch({ control, name: "alias" });
return ( return (
<> <>
<RedirectUrl id={alias} /> <RedirectUrl id={alias} />
<FormGroup <TextControl
name="alias"
label={t("alias")} label={t("alias")}
labelIcon={<HelpItem helpText={t("aliasHelp")} fieldLabelId="alias" />} labelIcon={t("aliasHelp")}
fieldId="alias" readOnly={isAliasReadonly}
isRequired rules={{
validated={ required: t("required"),
errors.alias ? ValidatedOptions.error : ValidatedOptions.default }}
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
isRequired
id="alias"
data-testid="alias"
isReadOnly={isAliasReadonly}
validated={
errors.alias ? ValidatedOptions.error : ValidatedOptions.default
}
{...register("alias", { required: true })}
/> />
</FormGroup>
<TextField <TextControl name="displayName" label={t("displayName")} />
field="displayName"
label="displayName"
data-testid="displayName"
/>
<DisplayOrder /> <DisplayOrder />
{isAliasReadonly ? ( {isAliasReadonly && (
<FormGroup <FormGroup
label={t("endpoints")} label={t("endpoints")}
fieldId="endpoints" fieldId="endpoints"
@ -78,7 +54,7 @@ export const SamlGeneralSettings = ({
isInline isInline
/> />
</FormGroup> </FormGroup>
) : null} )}
</> </>
); );
}; };

View file

@ -44,7 +44,7 @@ export const SelectControl = <
label, label,
options, options,
controller, controller,
variant, variant = SelectVariant.single,
labelIcon, labelIcon,
...rest ...rest
}: SelectControlProps<T, P>) => { }: SelectControlProps<T, P>) => {

View file

@ -1,4 +1,4 @@
import { ValidatedOptions } from "@patternfly/react-core"; import { TextAreaProps, ValidatedOptions } from "@patternfly/react-core";
import { import {
FieldPath, FieldPath,
FieldValues, FieldValues,
@ -13,11 +13,12 @@ import { KeycloakTextArea } from "./keycloak-text-area/KeycloakTextArea";
export type TextAreaControlProps< export type TextAreaControlProps<
T extends FieldValues, T extends FieldValues,
P extends FieldPath<T> = FieldPath<T>, P extends FieldPath<T> = FieldPath<T>,
> = UseControllerProps<T, P> & { > = UseControllerProps<T, P> &
TextAreaProps & {
label: string; label: string;
labelIcon?: string; labelIcon?: string;
isDisabled?: boolean; isDisabled?: boolean;
}; };
export const TextAreaControl = < export const TextAreaControl = <
T extends FieldValues, T extends FieldValues,