Skip to content
Snippets Groups Projects

Resolve "[MIN-56] Autocomplete input"

Merged Piotr Gawron requested to merge 24-min-56-autocomplete-input into development
17 files
+ 561
85
Compare changes
  • Side-by-side
  • Inline
Files
17
 
import {
 
autocompleteChemicalSelector,
 
autocompleteDrugSelector,
 
autocompleteSearchSelector,
 
} from '@/redux/autocomplete/autocomplete.selectors';
import {
import {
currentSelectedSearchElement,
currentSelectedSearchElement,
searchDrawerOpenSelector,
searchDrawerOpenSelector,
@@ -14,23 +19,33 @@ import {
@@ -14,23 +19,33 @@ import {
import { getSearchData } from '@/redux/search/search.thunks';
import { getSearchData } from '@/redux/search/search.thunks';
import Image from 'next/image';
import Image from 'next/image';
import { useRouter } from 'next/router';
import { useRouter } from 'next/router';
import { ChangeEvent, KeyboardEvent, useCallback, useEffect, useState } from 'react';
import { useCallback, KeyboardEvent, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useSelector } from 'react-redux';
import { ONE, ZERO } from '@/constants/common';
import { FIVE, ONE, ZERO } from '@/constants/common';
 
import Autosuggest from 'react-autosuggest';
import { clearEntityNumberData } from '@/redux/entityNumber/entityNumber.slice';
import { clearEntityNumberData } from '@/redux/entityNumber/entityNumber.slice';
import { getDefaultSearchTab, getSearchValuesArrayAndTrimToSeven } from './SearchBar.utils';
import { getDefaultSearchTab, getSearchValuesArrayAndTrimToSeven } from './SearchBar.utils';
 
import './autocomplete.css';
 
 
type Suggestion = {
 
name: string;
 
};
const ENTER_KEY_CODE = 'Enter';
const ENTER_KEY_CODE = 'Enter';
export const SearchBar = (): JSX.Element => {
export const SearchBar = (): JSX.Element => {
const [searchValue, setSearchValue] = useState<string>('');
const [searchValue, setSearchValue] = useState<string>('');
 
const [filteredSuggestions, setFilteredSuggestions] = useState<Suggestion[]>([]);
const isPendingSearchStatus = useSelector(isPendingSearchStatusSelector);
const isPendingSearchStatus = useSelector(isPendingSearchStatusSelector);
const isSearchDrawerOpen = useSelector(searchDrawerOpenSelector);
const isSearchDrawerOpen = useSelector(searchDrawerOpenSelector);
const isPerfectMatch = useSelector(perfectMatchSelector);
const isPerfectMatch = useSelector(perfectMatchSelector);
const searchValueState = useSelector(searchValueSelector);
const searchValueState = useSelector(searchValueSelector);
const currentTab = useSelector(currentSelectedSearchElement);
const searchAutocompleteState = useSelector(autocompleteSearchSelector);
 
const drugAutocompleteState = useSelector(autocompleteDrugSelector);
 
const chemicalAutocompleteState = useSelector(autocompleteChemicalSelector);
const dispatch = useAppDispatch();
const dispatch = useAppDispatch();
const router = useRouter();
const router = useRouter();
 
const currentTab = useSelector(currentSelectedSearchElement);
const updateSearchValueFromQueryParam = useCallback((): void => {
const updateSearchValueFromQueryParam = useCallback((): void => {
const { searchValue: searchValueQueryParam } = router.query;
const { searchValue: searchValueQueryParam } = router.query;
@@ -53,10 +68,6 @@ export const SearchBar = (): JSX.Element => {
@@ -53,10 +68,6 @@ export const SearchBar = (): JSX.Element => {
}
}
};
};
const onSearchChange = (event: ChangeEvent<HTMLInputElement>): void => {
setSearchValue(event.target.value);
};
const onSearchClick = (): void => {
const onSearchClick = (): void => {
const searchValues = getSearchValuesArrayAndTrimToSeven(searchValue);
const searchValues = getSearchValuesArrayAndTrimToSeven(searchValue);
@@ -82,6 +93,60 @@ export const SearchBar = (): JSX.Element => {
@@ -82,6 +93,60 @@ export const SearchBar = (): JSX.Element => {
openSearchDrawerIfClosed(currentTab);
openSearchDrawerIfClosed(currentTab);
};
};
 
const suggestions = searchAutocompleteState.searchValues
 
.concat(drugAutocompleteState.searchValues, chemicalAutocompleteState.searchValues)
 
.map(entry => {
 
return { name: entry };
 
})
 
.sort((a: Suggestion, b: Suggestion) => a.name.localeCompare(b.name));
 
 
const getSuggestions = (value: string): Suggestion[] => {
 
const inputValue = value.trim().toLowerCase();
 
const inputLength = inputValue.length;
 
if (inputLength === ZERO) {
 
return [];
 
}
 
return suggestions
 
.filter(lang => lang.name.toLowerCase().slice(ZERO, inputLength) === inputValue)
 
.slice(ZERO, FIVE);
 
};
 
 
const renderSuggestion = (suggestion: Suggestion): JSX.Element => {
 
return <div>{suggestion.name}</div>;
 
};
 
 
// Autosuggest will call this function every time you need to update suggestions.
 
// You already implemented this logic above, so just use it.
 
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 
// @ts-ignore
 
const onSuggestionsFetchRequested = ({ value }): void => {
 
setFilteredSuggestions(getSuggestions(value));
 
};
 
 
// Autosuggest will call this function every time you need to clear suggestions.
 
const onSuggestionsClearRequested = (): void => {
 
setFilteredSuggestions([]);
 
};
 
 
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 
// @ts-ignore
 
const onChange = (event, { newValue }): void => {
 
setSearchValue(newValue);
 
};
 
// Autosuggest will pass through all these props to the input.
 
const inputProps = {
 
placeholder: '',
 
value: searchValue,
 
name: 'search-input',
 
onChange,
 
onKeyDown: handleKeyPress,
 
onClick: handleSearchClick,
 
'data-testid': 'search-input',
 
disabled: isPendingSearchStatus,
 
};
 
 
const getSuggestionValue = (suggestion: Suggestion): string => suggestion.name;
 
useEffect(() => {
useEffect(() => {
updateSearchValueFromQueryParam();
updateSearchValueFromQueryParam();
}, [updateSearchValueFromQueryParam]);
}, [updateSearchValueFromQueryParam]);
@@ -89,19 +154,34 @@ export const SearchBar = (): JSX.Element => {
@@ -89,19 +154,34 @@ export const SearchBar = (): JSX.Element => {
clearSearchValueFromClearedState();
clearSearchValueFromClearedState();
}, [clearSearchValueFromClearedState]);
}, [clearSearchValueFromClearedState]);
 
const theme = {
 
input:
 
'h-9 w-72 rounded-[64px] border border-transparent bg-cultured px-4 py-2.5 text-xs font-medium text-font-400 outline-none hover:border-greyscale-600 focus:border-greyscale-600',
 
container: 'react-autosuggest__container',
 
inputFocused: 'react-autosuggest__input--focused',
 
suggestionsContainer: 'react-autosuggest__suggestions-container',
 
suggestionsContainerOpen: 'react-autosuggest__suggestions-container--open',
 
suggestionsList: 'react-autosuggest__suggestions-list',
 
suggestion: 'react-autosuggest__suggestion',
 
suggestionFirst: 'react-autosuggest__suggestion--first',
 
suggestionHighlighted: 'bg-primary-100',
 
sectionContainer: 'react-autosuggest__section-container',
 
sectionContainerFirst: 'react-autosuggest__section-container--first',
 
sectionTitle: 'react-autosuggest__section-title',
 
};
 
return (
return (
<div className="relative" data-testid="search-bar">
<div className="relative mt-5" data-testid="search-bar">
<input
<Autosuggest
value={searchValue}
suggestions={filteredSuggestions}
name="search-input"
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
aria-label="search-input"
onSuggestionsClearRequested={onSuggestionsClearRequested}
data-testid="search-input"
getSuggestionValue={getSuggestionValue}
onKeyDown={handleKeyPress}
renderSuggestion={renderSuggestion}
onChange={onSearchChange}
inputProps={inputProps}
disabled={isPendingSearchStatus}
theme={theme}
onClick={handleSearchClick}
className="h-9 w-72 rounded-[64px] border border-transparent bg-cultured px-4 py-2.5 text-xs font-medium text-font-400 outline-none hover:border-greyscale-600 focus:border-greyscale-600"
/>
/>
 
<button
<button
disabled={isPendingSearchStatus}
disabled={isPendingSearchStatus}
type="button"
type="button"
Loading