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" type="url"
isRequired readOnly={readOnly}
validated={ rules={{
errors.config?.authorizationUrl required: t("required"),
? ValidatedOptions.error }}
: ValidatedOptions.default />
} <TextControl
helperTextInvalid={t("required")} name="config.tokenUrl"
>
<KeycloakTextInput
type="url"
data-testid="authorizationUrl"
id="kc-authorization-url"
validated={
errors.config?.authorizationUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
isReadOnly={readOnly}
{...register("config.authorizationUrl", { required: true })}
/>
</FormGroup>
<FormGroup
label={t("tokenUrl")} label={t("tokenUrl")}
fieldId="tokenUrl" type="url"
isRequired readOnly={readOnly}
validated={ rules={{
errors.config?.tokenUrl required: t("required"),
? ValidatedOptions.error }}
: ValidatedOptions.default
}
helperTextInvalid={t("required")}
>
<KeycloakTextInput
type="url"
id="tokenUrl"
data-testid="tokenUrl"
validated={
errors.config?.tokenUrl
? ValidatedOptions.error
: ValidatedOptions.default
}
isReadOnly={readOnly}
{...register("config.tokenUrl", { required: true })}
/>
</FormGroup>
<TextField
field="config.logoutUrl"
label="logoutUrl"
isReadOnly={readOnly}
/> />
<TextField <TextControl
field="config.userInfoUrl" name="config.logoutUrl"
label="userInfoUrl" label={t("logoutUrl")}
isReadOnly={readOnly} readOnly={readOnly}
/> />
<TextField field="config.issuer" label="issuer" isReadOnly={readOnly} /> <TextControl
<SwitchField name="config.userInfoUrl"
field="config.validateSignature" label={t("userInfoUrl")}
label="validateSignature" readOnly={readOnly}
isReadOnly={readOnly} />
<TextControl
name="config.issuer"
label={t("issuer")}
readOnly={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")} <TextControl
/> name="config.publicKeySignatureVerifierKeyId"
</FormGroupField> label={t("validatingPublicKeyId")}
<TextField readOnly={readOnly}
field="config.publicKeySignatureVerifierKeyId"
label="validatingPublicKeyId"
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" name="config.pkceMethod"
label={t("pkceMethod")} label={t("pkceMethod")}
labelIcon={ labelIcon={t("pkceMethodHelp")}
<HelpItem controller={{
helpText={t("pkceMethodHelp")} defaultValue: PKCE_METHODS[0],
fieldLabelId="pkceMethod" }}
/> options={PKCE_METHODS.map((option) => ({
} key: option,
fieldId="pkceMethod" value: t(`${option}`),
> }))}
<Controller />
name="config.pkceMethod"
defaultValue={PKCE_METHODS[0]}
control={control}
render={({ field }) => (
<Select
toggleId="pkceMethod"
required
direction="down"
onToggle={() => setPkceMethodOpen(!pkceMethodOpen)}
onSelect={(_, value) => {
field.onChange(value as string);
setPkceMethodOpen(false);
}}
selections={t(`${field.value}`)}
variant={SelectVariant.single}
aria-label={t("pkceMethod")}
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={ defaultValue={`${environment.authServerUrl}/realms/${realm}`}
<HelpItem rules={{
helpText={t("serviceProviderEntityIdHelp")} required: t("required"),
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}`}
{...register("config.entityId", { required: true })}
/>
</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
field="displayName"
label="displayName"
data-testid="displayName"
/> />
<TextControl name="displayName" label={t("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> &
label: string; TextAreaProps & {
labelIcon?: string; label: string;
isDisabled?: boolean; labelIcon?: string;
}; isDisabled?: boolean;
};
export const TextAreaControl = < export const TextAreaControl = <
T extends FieldValues, T extends FieldValues,