Skip to content

Commit

Permalink
Fix duplicate autocomplete suggestions.
Browse files Browse the repository at this point in the history
Fixes watchdogpolska#990.

- removes duplicate autocomplete suggestions - the same item was present
  both in the initial fetch (fetching already associated items), and
  subsequent fetches (actual autocomplete).
- added some basic documentation to autocomplete-related coponents.
  • Loading branch information
rwakulszowa committed Jun 15, 2021
1 parent 6a5d7e1 commit 36eb6a2
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 1 deletion.
6 changes: 6 additions & 0 deletions frontend-project/src/components/FetchLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import { Awaited, KeysWithValsOfType } from '../services/common';
import { ResourceWithId } from '../services/service';
import { QQ } from '../utils/QQ';

/**
* Given an id, queries the Autocomplete API to get a relevant human-readable name.
*
* Technically speaking, the component doesn't perform any autocompletion. It communicates
* with the autocomplete API to have a single source of truth for (id => name) mappings.
*/
export function FetchLink<
T extends Awaited<ReturnType<AutocompleteServiceType[keyof AutocompleteServiceType]>>[number] & {
name?: string;
Expand Down
19 changes: 18 additions & 1 deletion frontend-project/src/components/FetchSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import { openNotificationWithIcon } from '../models/global';
import { AutocompleteFunctionType, AutocompleteServiceType } from '../services/autocomplete';
import { Awaited, KeysWithValsOfType, OptionType } from '../services/common';
import { QQ } from '../utils/QQ';
import { unionBy, sortBy } from 'lodash';

/**
* Select field with autocompletion.
*
* Whenever a user types into the search field, the component will communicate with the backend
* to show items matching the input.
*/
export function FetchSelect<
Mode extends 'multiple' | 'tags' | undefined,
OptionsType extends OptionType<string, Mode extends 'tags' ? string : number>,
Expand Down Expand Up @@ -52,6 +59,10 @@ export function FetchSelect<
);
}

// Call the autocomplete api once to convert field ids, fetched from the object's detail endpoint,
// into human readable names.
// Uses the autocomplete API for (id => name) mapping for consistency - subsequent calls, triggered
// by user input, will receive (id, name) pairs from the same API.
useEffect(() => {
const arrayValue = Array.isArray(value) ? value : [value];
if (
Expand All @@ -75,6 +86,8 @@ export function FetchSelect<
.finally(() => setFetching(false));
}, []);

// Fetch (id, name) pairs matching the search string.
// Invoked on every keystroke, after a short delay.
const debounceFetcher = (search: string) => {
if (!search) return [];
setAutocompleteOptions([]);
Expand All @@ -93,6 +106,10 @@ export function FetchSelect<
);
};

// All options to display to the user.
// Sets may overlap - remove duplicates to avoid duplicate rendering issues.
const options = sortBy(unionBy(shownOptions, autocompleteOptions, 'value'), 'label');

return (
<Select<OptionsType>
showSearch
Expand All @@ -101,7 +118,7 @@ export function FetchSelect<
onSearch={debounceFetcher}
notFoundContent={isFetching ? <Spin size="small" /> : null}
{...props}
options={[...shownOptions, ...autocompleteOptions]}
options={options}
value={value}
/>
);
Expand Down

0 comments on commit 36eb6a2

Please sign in to comment.