1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-10 00:05:24 +02:00

fix(wizard): debounce name state [EE-4177] (#8042)

move debouncing to the component (from the validation).
debounce returns undefined when it's not calling the debounced function,
and undefined is considered a validation error.
This commit is contained in:
Chaim Lev-Ari 2022-11-21 19:33:08 +02:00 committed by GitHub
parent 253a3a2b40
commit 7006c17ce4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 74 additions and 58 deletions

View file

@ -12,7 +12,7 @@ import {
EdgeTypes,
} from '@/react/portainer/environments/types';
import { EnvironmentGroupId } from '@/react/portainer/environments/environment-groups/types';
import { useDebounce } from '@/react/hooks/useDebounce';
import { useDebouncedValue } from '@/react/hooks/useDebouncedValue';
import {
refetchIfAnyOffline,
useEnvironmentList,
@ -75,7 +75,7 @@ export function EnvironmentList({ onClickItem, onRefresh }: Props) {
const [searchBarValue, setSearchBarValue] = useSearchBarState(storageKey);
const [pageLimit, setPageLimit] = usePaginationLimitState(storageKey);
const [page, setPage] = useState(1);
const debouncedTextFilter = useDebounce(searchBarValue);
const debouncedTextFilter = useDebouncedValue(searchBarValue);
const [connectionTypes, setConnectionTypes] = useHomePageFilter<
Filter<ConnectionType>[]

View file

@ -1,30 +1,45 @@
import { Field, useField } from 'formik';
import { useField } from 'formik';
import { string } from 'yup';
import { useRef } from 'react';
import _ from 'lodash';
import { getEnvironments } from '@/react/portainer/environments/environment.service';
import { useDebounce } from '@/react/hooks/useDebounce';
import { FormControl } from '@@/form-components/FormControl';
import { Input } from '@@/form-components/Input';
interface Props {
readonly?: boolean;
tooltip?: string;
placeholder?: string;
}
export function NameField({ readonly }: Props) {
const [, meta] = useField('name');
export function NameField({
readonly,
tooltip,
placeholder = 'e.g. docker-prod01 / kubernetes-cluster01',
}: Props) {
const [{ value }, meta, { setValue }] = useField('name');
const id = 'name-input';
const [debouncedValue, setDebouncedValue] = useDebounce(value, setValue);
return (
<FormControl label="Name" required errors={meta.error} inputId={id}>
<Field
<FormControl
label="Name"
required
errors={meta.error}
inputId={id}
tooltip={tooltip}
>
<Input
id={id}
name="name"
as={Input}
onChange={(e) => setDebouncedValue(e.target.value)}
value={debouncedValue}
data-cy="endpointCreate-nameInput"
placeholder="e.g. docker-prod01 / kubernetes-cluster01"
placeholder={placeholder}
readOnly={readonly}
/>
</FormControl>
@ -37,26 +52,30 @@ export async function isNameUnique(name = '') {
}
try {
const result = await getEnvironments({ limit: 1, query: { name } });
if (result.totalCount > 0) {
return false;
}
const result = await getEnvironments({
limit: 1,
query: { name, excludeSnapshots: true },
});
return (
result.totalCount === 0 || result.value.every((e) => e.Name !== name)
);
} catch (e) {
// if backend fails to respond, assume name is unique, name validation happens also in the backend
return true;
}
return true;
}
function cacheTest(
asyncValidate: (val?: string) => Promise<boolean> | undefined
) {
let valid = false;
let valid = true;
let value = '';
return async (newValue = '') => {
if (newValue !== value) {
const response = await asyncValidate(newValue);
value = newValue;
const response = await asyncValidate(newValue);
valid = !!response;
}
return valid;
@ -64,7 +83,7 @@ function cacheTest(
}
export function useNameValidation() {
const uniquenessTest = useRef(cacheTest(_.debounce(isNameUnique, 300)));
const uniquenessTest = useRef(cacheTest(isNameUnique));
return string()
.required('Name is required')