fix: searching on relationship fields properly fetches results

* feat: adds relationship field to test searchable input

* fix: searching on relationship fields properly fetches results

* chore: more dry relationship field
This commit is contained in:
James Mikrut
2021-04-18 15:27:34 -04:00
committed by GitHub
parent b383eb65c6
commit b86c3daa99
5 changed files with 73 additions and 52 deletions

View File

@@ -14,6 +14,7 @@ import { PaginatedDocs } from '../../../../../collections/config/types';
import { useFormProcessing } from '../../Form/context';
import optionsReducer from './optionsReducer';
import { Props, OptionsPage, Option, ValueWithRelation } from './types';
import useDebounce from '../../../../hooks/useDebounce';
import './index.scss';
@@ -57,6 +58,7 @@ const Relationship: React.FC<Props> = (props) => {
const [search, setSearch] = useState('');
const [errorLoading, setErrorLoading] = useState(false);
const [hasLoadedFirstOptions, setHasLoadedFirstOptions] = useState(false);
const debouncedSearch = useDebounce(search, 120);
const memoizedValidate = useCallback((value) => {
const validationResult = validate(value, { required });
@@ -79,6 +81,41 @@ const Relationship: React.FC<Props> = (props) => {
dispatchOptions({ type: 'ADD', data, relation, hasMultipleRelations, collection });
}, [collections, hasMultipleRelations]);
const getResults = useCallback(({ relations: relationsArg, lastLoadedPage: lastLoadedPageArg }) => {
if (relationsArg.length > 0) {
some(relationsArg, async (relation, callback) => {
const collection = collections.find((coll) => coll.slug === relation);
const fieldToSearch = collection?.admin?.useAsTitle || 'id';
const searchParam = search ? `&where[${fieldToSearch}][like]=${search}` : '';
const response = await fetch(`${serverURL}${api}/${relation}?limit=${maxResultsPerRequest}&page=${lastLoadedPageArg}&depth=0${searchParam}`);
const data: PaginatedDocs = await response.json();
if (response.ok) {
if (data.hasNextPage) {
return callback(false, {
data,
relation,
});
}
return callback({ relation, data });
}
return setErrorLoading(true);
}, (lastPage: OptionsPage, nextPage: OptionsPage) => {
if (nextPage) {
const { data, relation } = nextPage;
addOptions(data, relation);
setLastLoadedPage((l) => l + 1);
} else {
const { data, relation } = lastPage;
addOptions(data, relation);
setLastFullyLoadedRelation(relations.indexOf(relation));
setLastLoadedPage(1);
}
});
}
}, [addOptions, api, collections, relations, search, serverURL]);
const getNextOptions = useCallback((params = {} as Record<string, unknown>) => {
const clear = params?.clear;
@@ -95,41 +132,12 @@ const Relationship: React.FC<Props> = (props) => {
if (!errorLoading) {
const relationsToSearch = lastFullyLoadedRelation === -1 ? relations : relations.slice(lastFullyLoadedRelation + 1);
if (relationsToSearch.length > 0) {
some(relationsToSearch, async (relation, callback) => {
const collection = collections.find((coll) => coll.slug === relation);
const fieldToSearch = collection?.admin?.useAsTitle || 'id';
const searchParam = search ? `&where[${fieldToSearch}][like]=${search}` : '';
const response = await fetch(`${serverURL}${api}/${relation}?limit=${maxResultsPerRequest}&page=${lastLoadedPage}${searchParam}`);
const data: PaginatedDocs = await response.json();
if (response.ok) {
if (data.hasNextPage) {
return callback(false, {
data,
relation,
});
}
return callback({ relation, data });
}
return setErrorLoading(true);
}, (lastPage: OptionsPage, nextPage: OptionsPage) => {
if (nextPage) {
const { data, relation } = nextPage;
addOptions(data, relation);
setLastLoadedPage(lastLoadedPage + 1);
} else {
const { data, relation } = lastPage;
addOptions(data, relation);
setLastFullyLoadedRelation(relations.indexOf(relation));
setLastLoadedPage(1);
}
});
}
getResults({
relations: relationsToSearch,
lastLoadedPage,
});
}
}, [addOptions, api, collections, errorLoading, lastFullyLoadedRelation, lastLoadedPage, relations, required, search, serverURL]);
}, [errorLoading, required, lastFullyLoadedRelation, relations, getResults, lastLoadedPage]);
const findOptionsByValue = useCallback((): Option | Option[] => {
if (value) {
@@ -191,13 +199,11 @@ const Relationship: React.FC<Props> = (props) => {
const handleInputChange = useCallback((newSearch) => {
if (search !== newSearch) {
setSearch(newSearch);
setLastFullyLoadedRelation(-1);
setLastLoadedPage(1);
}
}, [search]);
const addOptionByID = useCallback(async (id, relation) => {
if (!errorLoading) {
if (!errorLoading && id !== 'null') {
const response = await fetch(`${serverURL}${api}/${relation}/${id}?depth=0`);
if (response.ok) {
@@ -248,6 +254,7 @@ const Relationship: React.FC<Props> = (props) => {
addOptions(data, relation);
if (!data.hasNextPage) {
setLastFullyLoadedRelation(relations.indexOf(relation));
} else {
@@ -261,6 +268,23 @@ const Relationship: React.FC<Props> = (props) => {
getFirstResults();
}, [addOptions, api, relations, serverURL]);
useEffect(() => {
if (debouncedSearch) {
dispatchOptions({
type: 'REPLACE',
payload: required ? [] : [{ value: 'null', label: 'None' }],
});
setLastLoadedPage(1);
setLastFullyLoadedRelation(-1);
getResults({
relations,
lastLoadedPage: 1,
});
}
}, [getResults, debouncedSearch, relations, required]);
const classes = [
'field-type',
baseClass,

View File

@@ -34,7 +34,6 @@ const useFieldType = <T extends unknown>(options: Options): FieldType<T> => {
// Get field by path
const field = getField(path);
const fieldExists = Boolean(field);
const initialValue = field?.initialValue;

View File

@@ -77,6 +77,10 @@ const traverseFields = (args: Arguments): void => {
dataCopy[field.name] = null;
}
if (field.type === 'relationship' && field.hasMany && (data[field.name]?.[0] === '' || data[field.name]?.[0] === 'none' || data[field.name]?.[0] === 'null')) {
dataCopy[field.name] = [];
}
if (field.type === 'number' && typeof data[field.name] === 'string') {
dataCopy[field.name] = parseFloat(data[field.name]);
}