UI - Update validation pattern on SSO settings form (#25387)

## For #25264 


https://github.com/user-attachments/assets/031f3636-3c0e-4439-969d-716cb08b2449

- [x] Changes file added for user-visible changes in `changes/`
- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
This commit is contained in:
jacobshandling 2025-01-13 14:47:02 -08:00 committed by GitHub
parent 80f503ab6a
commit 8c4275a467
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 76 additions and 43 deletions

View file

@ -0,0 +1,2 @@
- Fix form validation behavior on the SSO settings form.
-

View file

@ -32,6 +32,50 @@ interface ISsoFormErrors {
idp_name?: string | null;
}
const validate = (formData: ISsoFormData) => {
const errors: ISsoFormErrors = {};
const {
enableSso,
idpImageUrl,
metadata,
metadataUrl,
entityId,
idpName,
} = formData;
if (enableSso) {
if (idpImageUrl && !validUrl({ url: idpImageUrl })) {
errors.idp_image_url = `${idpImageUrl} is not a valid URL`;
}
if (!metadata) {
if (!metadataUrl) {
errors.metadata_url = "Metadata or Metadata URL must be present";
errors.metadata = "Metadata or Metadata URL must be present";
} else if (
!validUrl({ url: metadataUrl, protocols: ["http", "https"] })
) {
errors.metadata_url = `${metadataUrl} is not a valid URL`;
}
}
if (!entityId) {
errors.entity_id = "Entity ID must be present";
}
if (typeof entityId === "string" && entityId.length < 5) {
errors.entity_id = "Entity ID must be 5 or more characters";
}
if (!idpName) {
errors.idp_name = "Identity provider name must be present";
}
}
return errors;
};
const Sso = ({
appConfig,
handleSubmit,
@ -64,51 +108,35 @@ const Sso = ({
const [formErrors, setFormErrors] = useState<ISsoFormErrors>({});
const onInputChange = ({ name, value }: IFormField) => {
setFormData({ ...formData, [name]: value });
const newFormData = { ...formData, [name]: value };
setFormData(newFormData);
const newErrs = validate(newFormData);
// only set errors that are updates of existing errors
// new errors are only set onBlur or submit
const errsToSet: Record<string, string> = {};
Object.keys(formErrors).forEach((k) => {
// @ts-ignore
if (newErrs[k]) {
// @ts-ignore
errsToSet[k] = newErrs[k];
}
});
setFormErrors(errsToSet);
};
const validateForm = () => {
const errors: ISsoFormErrors = {};
if (enableSso) {
if (idpImageUrl && !validUrl({ url: idpImageUrl })) {
errors.idp_image_url = `${idpImageUrl} is not a valid URL`;
}
if (!metadata) {
if (!metadataUrl) {
errors.metadata_url = "Metadata or Metadata URL must be present";
errors.metadata = "Metadata or Metadata URL must be present";
} else if (
!validUrl({ url: metadataUrl, protocols: ["http", "https"] })
) {
errors.metadata_url = `${metadataUrl} is not a valid URL`;
}
}
if (!entityId) {
errors.entity_id = "Entity ID must be present";
}
if (typeof entityId === "string" && entityId.length < 5) {
errors.entity_id = "Entity ID must be 5 or more characters";
}
if (!idpName) {
errors.idp_name = "Identity provider name must be present";
}
}
setFormErrors(errors);
const onInputBlur = () => {
setFormErrors(validate(formData));
};
useEffect(() => {
validateForm();
}, [idpImageUrl, metadata, metadataUrl, entityId, idpName]);
const onFormSubmit = (evt: React.MouseEvent<HTMLFormElement>) => {
evt.preventDefault();
const errs = validate(formData);
if (Object.keys(errs).length > 0) {
setFormErrors(errs);
return;
}
// Formatting of API not UI
const formDataToSubmit = {
sso_settings: {
@ -135,6 +163,7 @@ const Sso = ({
<form onSubmit={onFormSubmit} autoComplete="off">
<Checkbox
onChange={onInputChange}
onBlur={onInputBlur}
name="enableSso"
value={enableSso}
parseTarget
@ -147,7 +176,7 @@ const Sso = ({
name="idpName"
value={idpName}
parseTarget
onBlur={validateForm}
onBlur={onInputBlur}
error={formErrors.idp_name}
tooltip="A required human friendly name for the identity provider that will provide single sign-on authentication."
/>
@ -158,7 +187,7 @@ const Sso = ({
name="entityId"
value={entityId}
parseTarget
onBlur={validateForm}
onBlur={onInputBlur}
error={formErrors.entity_id}
tooltip="The required entity ID is a URI that you use to identify Fleet when configuring the identity provider."
/>
@ -168,7 +197,7 @@ const Sso = ({
name="idpImageUrl"
value={idpImageUrl}
parseTarget
onBlur={validateForm}
onBlur={onInputBlur}
error={formErrors.idp_image_url}
tooltip={`An optional link to an image such
as a logo for the identity provider.`}
@ -180,7 +209,7 @@ const Sso = ({
name="metadata"
value={metadata}
parseTarget
onBlur={validateForm}
onBlur={onInputBlur}
error={formErrors.metadata}
tooltip="Metadata XML provided by the identity provider."
/>
@ -196,12 +225,13 @@ const Sso = ({
name="metadataUrl"
value={metadataUrl}
parseTarget
onBlur={validateForm}
onBlur={onInputBlur}
error={formErrors.metadata_url}
tooltip="Metadata URL provided by the identity provider."
/>
<Checkbox
onChange={onInputChange}
onBlur={onInputBlur}
name="enableSsoIdpLogin"
value={enableSsoIdpLogin}
parseTarget
@ -211,6 +241,7 @@ const Sso = ({
{isPremiumTier && (
<Checkbox
onChange={onInputChange}
onBlur={onInputBlur}
name="enableJitProvisioning"
value={enableJitProvisioning}
parseTarget