Skip to content

Commit 12fbd31

Browse files
authored
fix validation name, use hook (#823)
* fix validation name, use hook --------- Signed-off-by: SOUISSI Maissa (Externe) <[email protected]>
1 parent 46feffb commit 12fbd31

File tree

2 files changed

+129
-87
lines changed

2 files changed

+129
-87
lines changed

src/components/inputs/reactHookForm/text/UniqueNameInput.tsx

Lines changed: 11 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
77

8-
import { ChangeEvent, useCallback, useEffect } from 'react';
8+
import { ChangeEvent } from 'react';
99
import { FormattedMessage } from 'react-intl';
1010
import {
1111
CircularProgress,
@@ -16,11 +16,10 @@ import {
1616
type Theme,
1717
} from '@mui/material';
1818
import { Check as CheckIcon } from '@mui/icons-material';
19-
import { useController, useFormContext } from 'react-hook-form';
19+
import { useController } from 'react-hook-form';
2020
import { UUID } from 'crypto';
21-
import { useDebounce } from '../../../../hooks';
22-
import { ElementType, FieldConstants } from '../../../../utils';
23-
import { elementAlreadyExists } from '../../../../services';
21+
import { ElementType } from '../../../../utils';
22+
import { useUniqueNameValidation } from '../../../../hooks/use-unique-name-validation';
2423

2524
export interface UniqueNameInputProps {
2625
name: string;
@@ -39,7 +38,7 @@ export interface UniqueNameInputProps {
3938
}
4039

4140
/**
42-
* Input component that constantly check if the field's value is available or not
41+
* Input component that constantly checks if the field's value is available or not
4342
*/
4443
export function UniqueNameInput({
4544
name,
@@ -55,93 +54,18 @@ export function UniqueNameInput({
5554
}: Readonly<UniqueNameInputProps>) {
5655
const {
5756
field: { onChange, onBlur, value, ref },
58-
fieldState: { error, isDirty },
57+
fieldState: { error },
5958
} = useController({
6059
name,
6160
});
6261

63-
const {
64-
field: { value: selectedDirectory },
65-
} = useController({
66-
name: FieldConstants.DIRECTORY,
62+
const { isValidating } = useUniqueNameValidation({
63+
name,
64+
currentName,
65+
elementType,
66+
activeDirectory,
6767
});
6868

69-
const {
70-
setError,
71-
clearErrors,
72-
trigger,
73-
formState: { errors },
74-
} = useFormContext();
75-
76-
// This is a trick to share the custom validation state among the form : while this error is present, we can't validate the form
77-
const isValidating = errors.root?.isValidating;
78-
79-
const directory = selectedDirectory || activeDirectory;
80-
81-
const handleCheckName = useCallback(
82-
(nameValue: string) => {
83-
if (nameValue !== currentName && directory) {
84-
elementAlreadyExists(directory, nameValue, elementType)
85-
.then((alreadyExist) => {
86-
if (alreadyExist) {
87-
setError(name, {
88-
type: 'validate',
89-
message: 'nameAlreadyUsed',
90-
});
91-
}
92-
})
93-
.catch((e) => {
94-
setError(name, {
95-
type: 'validate',
96-
message: 'nameValidityCheckErrorMsg',
97-
});
98-
console.error(e?.message);
99-
})
100-
.finally(() => {
101-
clearErrors('root.isValidating');
102-
/* force form to validate, otherwise form
103-
will remain invalid (setError('root.isValidating') invalid form and clearErrors does not affect form isValid state :
104-
see documentation : https://react-hook-form.com/docs/useform/clearerrors) */
105-
trigger('root.isValidating');
106-
});
107-
} else {
108-
trigger('root.isValidating');
109-
}
110-
},
111-
[currentName, directory, elementType, setError, name, clearErrors, trigger]
112-
);
113-
114-
const debouncedHandleCheckName = useDebounce(handleCheckName, 700);
115-
116-
// We have to use an useEffect because the name can change from outside of this component (when we upload a case file for instance)
117-
useEffect(() => {
118-
const trimmedValue = value.trim();
119-
120-
if (selectedDirectory) {
121-
debouncedHandleCheckName(trimmedValue);
122-
}
123-
124-
// if the name is unchanged, we don't do custom validation
125-
if (!isDirty) {
126-
clearErrors(name);
127-
return;
128-
}
129-
if (trimmedValue) {
130-
clearErrors(name);
131-
setError('root.isValidating', {
132-
type: 'validate',
133-
message: 'cantSubmitWhileValidating',
134-
});
135-
debouncedHandleCheckName(trimmedValue);
136-
} else {
137-
clearErrors('root.isValidating');
138-
setError(name, {
139-
type: 'validate',
140-
message: 'nameEmpty',
141-
});
142-
}
143-
}, [debouncedHandleCheckName, setError, clearErrors, name, value, isDirty, selectedDirectory]);
144-
14569
// Handle on user's change
14670
const handleManualChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
14771
onChange(e.target.value);
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* Copyright (c) 2024, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import { useCallback, useEffect } from 'react';
9+
import { useController, useFormContext } from 'react-hook-form';
10+
import { UUID } from 'crypto';
11+
import { ElementType, FieldConstants } from '../utils';
12+
import { useDebounce } from './useDebounce';
13+
import { elementAlreadyExists } from '../services';
14+
15+
interface UseUniqueNameValidationParams {
16+
name: string;
17+
currentName?: string;
18+
elementType: ElementType;
19+
activeDirectory?: string;
20+
elementExists?: (directory: UUID, name: string, type: ElementType) => Promise<boolean>;
21+
}
22+
23+
export function useUniqueNameValidation({
24+
name,
25+
currentName = '',
26+
elementType,
27+
activeDirectory,
28+
elementExists = elementAlreadyExists,
29+
}: UseUniqueNameValidationParams) {
30+
const {
31+
setError,
32+
clearErrors,
33+
trigger,
34+
formState: { errors, defaultValues },
35+
} = useFormContext();
36+
37+
const {
38+
field: { value },
39+
fieldState: { isDirty },
40+
} = useController({ name });
41+
42+
const {
43+
field: { value: selectedDirectory },
44+
} = useController({
45+
name: FieldConstants.DIRECTORY,
46+
});
47+
48+
const defaultFieldValue = defaultValues?.[name];
49+
const directory = selectedDirectory || activeDirectory;
50+
51+
// This is a trick to share the custom validation state among the form : while this error is present, we can't validate the form
52+
const isValidating = errors.root?.isValidating;
53+
54+
const handleCheckName = useCallback(
55+
(nameValue: string) => {
56+
if (nameValue !== currentName && directory) {
57+
elementExists(directory, nameValue, elementType)
58+
.then((alreadyExist) => {
59+
if (alreadyExist) {
60+
setError(name, {
61+
type: 'validate',
62+
message: 'nameAlreadyUsed',
63+
});
64+
}
65+
})
66+
.catch(() => {
67+
setError(name, {
68+
type: 'validate',
69+
message: 'nameValidityCheckErrorMsg',
70+
});
71+
})
72+
.finally(() => {
73+
clearErrors('root.isValidating');
74+
/* force form to validate, otherwise form
75+
will remain invalid (setError('root.isValidating') invalid form and clearErrors does not affect form isValid state :
76+
see documentation : https://react-hook-form.com/docs/useform/clearerrors) */
77+
trigger('root.isValidating');
78+
});
79+
} else {
80+
trigger('root.isValidating');
81+
}
82+
},
83+
[currentName, directory, elementType, name, setError, clearErrors, trigger, elementExists]
84+
);
85+
86+
const debouncedHandleCheckName = useDebounce(handleCheckName, 700);
87+
88+
// We have to use an useEffect because the name can change from outside of this component (when we upload a case file for instance)
89+
useEffect(() => {
90+
const trimmedValue = value?.trim?.();
91+
if (!trimmedValue) {
92+
clearErrors('root.isValidating');
93+
setError(name, {
94+
type: 'validate',
95+
message: 'nameEmpty',
96+
});
97+
return;
98+
}
99+
100+
if (!isDirty || defaultFieldValue?.trim?.() === trimmedValue) {
101+
clearErrors(name);
102+
return;
103+
}
104+
105+
setError('root.isValidating', {
106+
type: 'validate',
107+
message: 'cantSubmitWhileValidating',
108+
});
109+
110+
if (directory) {
111+
debouncedHandleCheckName(trimmedValue);
112+
}
113+
}, [value, debouncedHandleCheckName, setError, clearErrors, name, isDirty, defaultFieldValue, directory]);
114+
115+
return {
116+
isValidating,
117+
};
118+
}

0 commit comments

Comments
 (0)