From 1ab4d1d2904d79fef75465baec866d61b8d6d3ac Mon Sep 17 00:00:00 2001 From: jpfisher72 Date: Fri, 1 Nov 2024 16:48:38 -0400 Subject: [PATCH] Rework Gene Autocomplete --- .../gene-expression/geneexpression.tsx | 1 + .../_geneAutocomplete/GeneAutocomplete.tsx | 76 +++++++++++-------- .../src/app/search/_geneAutocomplete/types.ts | 2 - .../src/app/search/mainresultsfilters.tsx | 2 +- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/screen2.0/src/app/applets/gene-expression/geneexpression.tsx b/screen2.0/src/app/applets/gene-expression/geneexpression.tsx index 3eae15d2..85e75166 100644 --- a/screen2.0/src/app/applets/gene-expression/geneexpression.tsx +++ b/screen2.0/src/app/applets/gene-expression/geneexpression.tsx @@ -334,6 +334,7 @@ export function GeneExpression(props: { mm10 (props.slotProps?.autocompleteProps?.defaultValue) + const [inputValue, setInputValue] = useState("") const [options, setOptions] = useState([]) const [descriptions, setDescriptions] = useState<{ name: string; desc: string }[]>([]) const [loadingOptions, setLoadingOptions] = useState(false) - //Fetch gene desciptions + //Fetch and set gene desciptions. Descriptions should probably not be it's own state variable, but rather added to options useEffect(() => { const fetchData = async () => { const descriptions = await Promise.all( @@ -62,6 +60,7 @@ export const GeneAutocomplete = ( if (options) fetchData() }, [options]) + //handles setting options on search change const onSearchChange = async (value: string, assembly: string) => { setOptions([]) setLoadingOptions(true) @@ -91,15 +90,14 @@ export const GeneAutocomplete = ( } } }) - setOptions(g) + setOptions(g.sort((a, b) => a.name.localeCompare(b.name))) } else if (genesSuggestion && genesSuggestion.length === 0) { setOptions([]) } setLoadingOptions(false) } - const debounceFn = useMemo(() => debounce(onSearchChange, 500), []) - + const debounceOnSearchChange = useMemo(() => debounce(onSearchChange, 500), []) // Merge the ListboxProps const mergedListboxProps = { @@ -141,39 +139,57 @@ export const GeneAutocomplete = ( ...slotProps?.inputTextFieldProps?.sx } } + + //changes to text input + const handleInputChange = ( + _, + value: string, + ) => { + if (value != "") { debounceOnSearchChange(value, assembly) } //fetch new gene suggestions + setInputValue(value) + } - const attemptSubmit = (inputVal: string) => { - const gene = options.find(x => x.name.toLowerCase() === inputVal.toLowerCase()) - if (gene) { - setInputValue(gene.name) - if (onGeneSubmitted) onGeneSubmitted(gene) - } + //changes to value of component + const handleChange = ( + _, + value: GeneInfo, + ) => { + setValue(value) + if (onGeneSelected) onGeneSelected(value) } + //checks for enter key and tries to match with listed options + const handleKeyDown = useCallback(( + event: React.KeyboardEvent & { defaultMuiPrevented?: boolean;} + ) => { + if (event.key === "Enter") { + const matchingGene = options.find(x => x.name.toLowerCase() === inputValue.toLowerCase()) + if (matchingGene) { + setValue(matchingGene) + if (onGeneSubmitted) onGeneSubmitted(matchingGene) + } + } + }, [inputValue, onGeneSubmitted, options]) + + const handleIconClick = useCallback(() => { + if (onGeneSubmitted && value) onGeneSubmitted(value) + }, [onGeneSubmitted, value]) + return ( a.name.localeCompare(b.name))} //How do I type this properly? + options={options} + value={value} inputValue={inputValue} - onInputChange={(_, newInputValue) => { - if (newInputValue != "") { - debounceFn(newInputValue, assembly) // This triggers sending new request for genes - } - setInputValue(newInputValue) - }} - onChange={(_, value) => onGeneSelected && onGeneSelected(value)} //Should I just expose the whole onChange function? - onKeyDown={(event) => { - if (event.key === "Enter") { - attemptSubmit(inputValue) - } - }} + onInputChange={handleInputChange} + onChange={handleChange} //Should I just expose the whole onChange function? + onKeyDown={handleKeyDown} noOptionsText={loadingOptions ? "Loading..." : "No Genes Found"} //Maybe expose? renderInput={(params) => ( @@ -207,7 +223,7 @@ export const GeneAutocomplete = ( attemptSubmit(inputValue)} + onClick={handleIconClick} {...mergedIconProps} > {CustomEndIcon ? diff --git a/screen2.0/src/app/search/_geneAutocomplete/types.ts b/screen2.0/src/app/search/_geneAutocomplete/types.ts index bae2cc8a..a461e1e1 100644 --- a/screen2.0/src/app/search/_geneAutocomplete/types.ts +++ b/screen2.0/src/app/search/_geneAutocomplete/types.ts @@ -49,8 +49,6 @@ endIcon?: 'search' | 'add' | 'none' CustomEndIcon?: OverridableComponent> & { muiName: string; } -// onIconClick?: React.MouseEventHandler -onTextBoxClick?: React.MouseEventHandler /** * * Callback fired when a gene is selected from the dropdown options. Not fired on submission (enter, clicking endIcon) diff --git a/screen2.0/src/app/search/mainresultsfilters.tsx b/screen2.0/src/app/search/mainresultsfilters.tsx index 741ec7f2..497ac1eb 100644 --- a/screen2.0/src/app/search/mainresultsfilters.tsx +++ b/screen2.0/src/app/search/mainresultsfilters.tsx @@ -919,9 +919,9 @@ export function MainResultsFilters( null, getOptionDisabled: option => !linkedGenesWithNums.find(x => x.geneName === option.name), }, + inputTextFieldProps: {onClick: () => !dataLinkedGenes && !loadingLinkedGenes && getLinkedGenes()}, stackProps: { mt: 1 } }} - onTextBoxClick={() => !dataLinkedGenes && !loadingLinkedGenes && getLinkedGenes()} endIcon="none" colorTheme="light" onGeneSelected={(gene) =>