From 79050af39191a8566162e976148f2f73b2510bf2 Mon Sep 17 00:00:00 2001 From: "kasia.oczkowska" Date: Fri, 25 Oct 2024 15:35:35 +0100 Subject: [PATCH 1/2] feat: introduce uniqueness validation for remote role name --- .../ControlledAutocomplete/index.jsx | 26 ++++++---- packages/web/src/components/Form/index.jsx | 10 +++- .../web/src/components/TextField/index.jsx | 4 ++ .../src/pages/Authentication/RoleMappings.jsx | 48 ++++++++++++++++++- .../RoleMappingsFieldsArray.jsx | 6 ++- 5 files changed, 81 insertions(+), 13 deletions(-) diff --git a/packages/web/src/components/ControlledAutocomplete/index.jsx b/packages/web/src/components/ControlledAutocomplete/index.jsx index 0ade2237..75f8cbf5 100644 --- a/packages/web/src/components/ControlledAutocomplete/index.jsx +++ b/packages/web/src/components/ControlledAutocomplete/index.jsx @@ -29,6 +29,8 @@ function ControlledAutocomplete(props) { options = [], dependsOn = [], showOptionValue, + renderInput, + showHelperText = true, ...autocompleteProps } = props; let dependsOnValues = []; @@ -105,16 +107,18 @@ function ControlledAutocomplete(props) { )} )} + renderInput={(params) => renderInput(params, fieldState)} /> - - - {fieldState.isTouched - ? fieldState.error?.message || description - : description} - + {showHelperText && ( + + {fieldState.isTouched + ? fieldState.error?.message || description + : description} + + )} )} /> @@ -132,6 +136,10 @@ ControlledAutocomplete.propTypes = { onBlur: PropTypes.func, onChange: PropTypes.func, options: PropTypes.array, + renderInput: PropTypes.func.isRequired, + showFormHelperText: PropTypes.bool, + showErrorTouched: PropTypes.bool, + showHelperText: PropTypes.bool, }; export default ControlledAutocomplete; diff --git a/packages/web/src/components/Form/index.jsx b/packages/web/src/components/Form/index.jsx index 574674d4..614873f7 100644 --- a/packages/web/src/components/Form/index.jsx +++ b/packages/web/src/components/Form/index.jsx @@ -13,12 +13,14 @@ function Form(props) { resolver, render, mode = 'all', + reValidateMode = 'onBlur', + automaticValidation = true, ...formProps } = props; const methods = useForm({ defaultValues, - reValidateMode: 'onBlur', + reValidateMode, resolver, mode, }); @@ -30,7 +32,9 @@ function Form(props) { * For fields having `dependsOn` fields, we need to re-validate the form. */ React.useEffect(() => { - methods.trigger(); + if (automaticValidation) { + methods.trigger(); + } }, [methods.trigger, form]); React.useEffect(() => { @@ -56,6 +60,8 @@ Form.propTypes = { render: PropTypes.func, resolver: PropTypes.func, mode: PropTypes.oneOf(['onChange', 'onBlur', 'onSubmit', 'onTouched', 'all']), + reValidateMode: PropTypes.oneOf(['onChange', 'onBlur', 'onSubmit']), + automaticValidation: PropTypes.bool, }; export default Form; diff --git a/packages/web/src/components/TextField/index.jsx b/packages/web/src/components/TextField/index.jsx index 20ec3aeb..dfe505c2 100644 --- a/packages/web/src/components/TextField/index.jsx +++ b/packages/web/src/components/TextField/index.jsx @@ -31,6 +31,7 @@ function TextField(props) { onBlur, onChange, 'data-test': dataTest, + showError = false, ...textFieldProps } = props; return ( @@ -47,6 +48,7 @@ function TextField(props) { onBlur: controllerOnBlur, ...field }, + fieldState: { error }, }) => ( )} /> @@ -89,6 +92,7 @@ TextField.propTypes = { disabled: PropTypes.bool, onBlur: PropTypes.func, onChange: PropTypes.func, + showError: PropTypes.bool, }; export default TextField; diff --git a/packages/web/src/pages/Authentication/RoleMappings.jsx b/packages/web/src/pages/Authentication/RoleMappings.jsx index 9bfff4db..66a14c38 100644 --- a/packages/web/src/pages/Authentication/RoleMappings.jsx +++ b/packages/web/src/pages/Authentication/RoleMappings.jsx @@ -5,6 +5,8 @@ import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar'; import { useMemo } from 'react'; +import { yupResolver } from '@hookform/resolvers/yup'; +import * as yup from 'yup'; import Form from 'components/Form'; import useFormatMessage from 'hooks/useFormatMessage'; @@ -23,6 +25,42 @@ function generateFormRoleMappings(roleMappings) { })); } +const uniqueRemoteRoleName = (array, context, formatMessage) => { + const seen = new Set(); + for (const [index, value] of array.entries()) { + if (seen.has(value.remoteRoleName)) { + const path = `${context.path}[${index}].remoteRoleName`; + return context.createError({ + message: `${formatMessage('roleMappingsForm.remoteRoleName')} must be unique`, + path, + }); + } + seen.add(value.remoteRoleName); + } + return true; +}; + +const getValidationSchema = (formatMessage) => + yup.object({ + roleMappings: yup + .array() + .of( + yup.object({ + roleId: yup + .string() + .required(`${formatMessage('roleMappingsForm.role')} is required`), + remoteRoleName: yup + .string() + .required( + `${formatMessage('roleMappingsForm.remoteRoleName')} is required`, + ), + }), + ) + .test('unique-remoteRoleName', '', (value, ctx) => { + return uniqueRemoteRoleName(value, ctx, formatMessage); + }), + }); + function RoleMappings({ provider, providerLoading }) { const formatMessage = useFormatMessage(); const enqueueSnackbar = useEnqueueSnackbar(); @@ -94,7 +132,15 @@ function RoleMappings({ provider, providerLoading }) { {formatMessage('roleMappingsForm.title')} -
+ ( + renderInput={(params, { error }) => ( )} loading={isRolesLoading} + showHelperText={false} /> Date: Fri, 8 Nov 2024 11:11:07 +0000 Subject: [PATCH 2/2] refactor: remove unnecessary prop types --- packages/web/src/components/ControlledAutocomplete/index.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/web/src/components/ControlledAutocomplete/index.jsx b/packages/web/src/components/ControlledAutocomplete/index.jsx index 75f8cbf5..ebb33beb 100644 --- a/packages/web/src/components/ControlledAutocomplete/index.jsx +++ b/packages/web/src/components/ControlledAutocomplete/index.jsx @@ -137,8 +137,6 @@ ControlledAutocomplete.propTypes = { onChange: PropTypes.func, options: PropTypes.array, renderInput: PropTypes.func.isRequired, - showFormHelperText: PropTypes.bool, - showErrorTouched: PropTypes.bool, showHelperText: PropTypes.bool, };