Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #487

Merged
merged 10 commits into from
Apr 4, 2024
193 changes: 167 additions & 26 deletions client/src/components/Shared/SearchBar/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import './SearchBar.scss';
import Autocomplete from '@mui/material/Autocomplete';
import {
Alert,
AlertTitle,
Box,
Button,
Checkbox,
FormControl,
FormControlLabel,
InputLabel,
MenuItem,
Select,
SelectChangeEvent,
TextField,
Tooltip,
} from '@mui/material';
import { useContext, useEffect } from 'react';
import React from 'react';
Expand All @@ -15,6 +22,13 @@ import { ActionTypes } from 'stores/Global/reducers';
import { useGetNameSuggestions } from 'hooks/queries/useGetNameSuggestions';
import { SearchTypes } from 'types/types';
import { useGetIsMobile } from 'hooks/shared/useGetIsMobile';
import HelpIcon from '@mui/icons-material/Help';

enum DelimiterTypes {
Comma = 'Comma',
CommaSpace = 'Comma With Space',
TabNewline = 'Tab or Newline',
}

type SearchBarProps = {
handleSubmit: () => void;
Expand All @@ -27,6 +41,10 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
state.interactionMode
);
const [typedSearchTerm, setTypedSearchTerm] = React.useState('');
const [pastingFromDocument, setPastingFromDocument] = React.useState(false);
const [pastedSearchDelimiter, setPastedSearchDelimiter] = React.useState('');
const [searchWasPasted, setSearchWasPasted] = React.useState(false);

const typeAheadQuery = useGetNameSuggestions(typedSearchTerm, searchType);
let autocompleteOptions = typeAheadQuery?.data?.geneSuggestions || [];
const drugAutocompleteOptions = typeAheadQuery?.data?.drugSuggestions || [];
Expand All @@ -37,9 +55,11 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {

// support searching for terms that the API may not return (add user's typed term to options if it's not already there)
if (
typedSearchTerm &&
autocompleteOptions.filter(
(option: { suggestion: string }) => option.suggestion === typedSearchTerm
).length === 0
).length === 0 &&
typedSearchTerm.trim() !== ''
) {
autocompleteOptions = [
{ suggestion: typedSearchTerm },
Expand All @@ -52,6 +72,8 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
const handleAutocompleteChange = (event: any, value: any) => {
if (value.length === 0) {
setSelectedOptions([]);
// for clearing the paste warning, if applicable
setSearchWasPasted(false);
dispatch({ type: ActionTypes.DeleteAllTerms });
} else {
setSelectedOptions(value);
Expand All @@ -76,33 +98,36 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {

const handleDemoClick = () => {
if (searchType === SearchTypes.Gene) {
setSelectedOptions([
{ suggestion: 'FLT1' },
{ suggestion: 'FLT2' },
{ suggestion: 'FLT3' },
{ suggestion: 'STK1' },
{ suggestion: 'MM1' },
{ suggestion: 'AQP1' },
{ suggestion: 'LOC100508755' },
{ suggestion: 'FAKE1' },
]);
const geneDemoList = [
'FLT1',
'FLT2',
'FLT3',
'STK1',
'MM1',
'AQP1',
'LOC100508755',
'FAKE1',
];
setSelectedOptions(convertToDropdownOptions(geneDemoList));
} else if (searchType === SearchTypes.Drug) {
setSelectedOptions([
{ suggestion: 'SUNITINIB' },
{ suggestion: 'ZALCITABINE' },
{ suggestion: 'TRASTUZUMAB' },
{ suggestion: 'NOTREAL' },
]);
const drugDemoList = [
'SUNITINIB',
'ZALCITABINE',
'TRASTUZUMAB',
'NOTREAL',
];
setSelectedOptions(convertToDropdownOptions(drugDemoList));
} else if (searchType === SearchTypes.Categories) {
setSelectedOptions([
{ suggestion: 'HER2' },
{ suggestion: 'ERBB2' },
{ suggestion: 'PTGDR' },
{ suggestion: 'EGFR' },
{ suggestion: 'RECK' },
{ suggestion: 'KCNMA1' },
{ suggestion: 'MM1' },
]);
const categoriesDemoList = [
'HER2',
'ERBB2',
'PTGDR',
'EGFR',
'RECK',
'KCNMA1',
'MM1',
];
setSelectedOptions(convertToDropdownOptions(categoriesDemoList));
}
};
const handleSearchClick = () => {
Expand All @@ -125,9 +150,77 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
}
}, [selectedOptions]);

const convertToDropdownOptions = (options: string[]) => {
return options.map((item: string) => {
return { suggestion: item.trim() };
});
};

const handlePaste = (event: any) => {
let pastedText = event.clipboardData.getData('text');
let pastedOptions: any[] = convertToDropdownOptions([pastedText]);

const commaSepOptions = pastedText.split(',');

if (pastedSearchDelimiter === DelimiterTypes.Comma) {
pastedOptions = convertToDropdownOptions(commaSepOptions);
} else if (pastedSearchDelimiter === DelimiterTypes.CommaSpace) {
const commaSpaceSepOptions = pastedText.split(', ');
pastedOptions = convertToDropdownOptions(commaSpaceSepOptions);
} else if (pastedSearchDelimiter === DelimiterTypes.TabNewline) {
const whitespaceRegex = /[\t\n\r\f\v]/;
const whitespaceSepOptions = pastedText.split(whitespaceRegex);
pastedOptions = convertToDropdownOptions(whitespaceSepOptions);
} else {
pastedOptions = convertToDropdownOptions(commaSepOptions);
}
setSearchWasPasted(true);
// make sure we persist the search terms already entered, combine any pre-existing search terms with the new pasted options
const newSearchOptions = selectedOptions.concat(pastedOptions);
// remove any duplicated terms (need to iterate through only the terms since objects are never equivalent in js, even if the contents are the same)
const uniqueSearchTerms = [
...new Set(newSearchOptions.map((option) => option.suggestion)),
];
setSelectedOptions(convertToDropdownOptions(uniqueSearchTerms));
// we don't want the code to also run what's in onInputChange for the Autocomplete since everything is handled here
event.preventDefault();
};

const handleCheckboxSelect = (event: any) => {
setPastingFromDocument(event.target.checked);
// reset the selected delimiter and searchWasPasted, to avoid potential weird behaviors if a user deselects the checkbox
setPastedSearchDelimiter('');
setSearchWasPasted(false);
};

const handleDelimiterChange = (event: any) => {
setPastedSearchDelimiter(event.target.value as string);
};

const pasteAlert =
searchWasPasted && pastedSearchDelimiter === '' ? (
<Box width="100%" pb={2}>
<Alert severity="info">
<AlertTitle>Verify your search terms</AlertTitle>
<p>
It looks like you pasted search terms. We have defaulted the
delimiter to comma-separated terms.
</p>
<p style={{ marginTop: '10px' }}>
If this is incorrect or you would like to use a different delimiter,
make sure to check the “Bulk search” option below and select a
delimiter from the drop down.
</p>
</Alert>
</Box>
) : (
<></>
);

return (
<>
<Box id="search-bar-container" width={isMobile ? '95%' : '75%'}>
{pasteAlert}
<Box display="flex" flexWrap={isMobile ? 'wrap' : 'nowrap'}>
<Box width={isMobile ? '100%' : 'fit-content'}>
<Select
Expand Down Expand Up @@ -155,6 +248,7 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
<Box>
<TextField
{...params}
onPaste={handlePaste}
variant="standard"
label="Search Terms"
/>
Expand Down Expand Up @@ -190,6 +284,53 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
</Button>
</Box>
</Box>
<Box
display="flex"
pt={5}
flexWrap="wrap"
height="100px"
alignContent="center"
justifyContent="end"
>
<Tooltip
title="Select this option if you are pasting terms from an external document"
arrow
>
<FormControlLabel
checked={pastingFromDocument}
onChange={handleCheckboxSelect}
control={<Checkbox />}
label="Bulk search"
/>
</Tooltip>
<Box
width={isMobile ? '100%' : '35%'}
display={pastingFromDocument ? '' : 'none'}
>
<FormControl fullWidth>
<InputLabel id="delimiter-select-label">
Select delimiter
</InputLabel>
<Select
labelId="delimiter-select-label"
id="delimiter-select"
value={pastedSearchDelimiter}
label="Select delimiter"
onChange={handleDelimiterChange}
>
<MenuItem value={DelimiterTypes.Comma}>
{DelimiterTypes.Comma}
</MenuItem>
<MenuItem value={DelimiterTypes.CommaSpace}>
{DelimiterTypes.CommaSpace}
</MenuItem>
<MenuItem value={DelimiterTypes.TabNewline}>
{DelimiterTypes.TabNewline}
</MenuItem>
</Select>
</FormControl>
</Box>
</Box>
</Box>
</>
);
Expand Down
16 changes: 6 additions & 10 deletions client/src/pages/About/SubSections/Contact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ export const Contact = () => {
<div className="contact-section-container doc-section">
<div>
<p>
DGIdb was developed at The McDonnell Genome Institute, Washington
University School of Medicine. If you have a source of information
related to the druggable genome you would like us to incorporate,
please contact us at{' '}
DGIdb was initially developed at The McDonnell Genome Institute,
Washington University School of Medicine. If you have a source of
information related to the druggable genome you would like us to
incorporate, please contact us at{' '}
<Link href="mailto:[email protected]">[email protected].</Link>
</p>
<p>
Expand All @@ -24,12 +24,8 @@ export const Contact = () => {

<div className="left-section">
<h4>
<Link
href="http://genome.wustl.edu/"
target="_blank"
rel="noreferrer"
>
The McDonnell Genome Institute
<Link href="https://griffithlab.org" target="_blank" rel="noreferrer">
The Griffith Laboratory
</Link>
</h4>
<p>Washington University</p>
Expand Down
10 changes: 10 additions & 0 deletions client/src/pages/About/SubSections/Publications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ import { Link } from '@mui/material';
export const Publications = () => {
return (
<div className="publications-section-container">
<div className="about-pub-item">
<Link href="https://pubmed.ncbi.nlm.nih.gov/37953380/" target="_blank">
DGIdb 5.0: rebuilding the drug-gene interaction database for precision
medicine and drug discovery platforms.
</Link>{' '}
Cannon M, Stevenson J, Stahl K, Basu R, Coffman A, Kiwala S, McMichael
JF, Kuzma K, Morrisey D, Cotto KC, Mardis ER, Griffith OL, Griffith M,
Wagner AH. Nucleic Acids Research. 2024 Jan 5; doi:
https://doi.org/10.1093/nar/gkad1040. PMID: 37953380.
</div>
<div className="about-pub-item">
<Link href="https://pubmed.ncbi.nlm.nih.gov/33237278/" target="_blank">
Integration of the Drug–Gene Interaction Database (DGIdb 4.0) with
Expand Down
Loading
Loading