Skip to content

Commit 4d9303b

Browse files
katiestahlacoffmanmcannon068nw
authoredApr 4, 2024
Staging (#488)
* Dev (#483) * switch multiple drug matching to use left anchored, case insensitive search * Update citations with V5 citation (#477) * update citations for v5 * fixed link * prettier * rudimentary support for querying current data_version * implement ga4gh service service info spec * update griffith lab affiliation * run prettier * fix: categories.tsv was downloading interactions (#475) * fix: categories.tsv was downloading interactions * feat: change top row of data downloads to latest * feat: change top row of data downloads to latest --------- Co-authored-by: Adam Coffman <[email protected]> Co-authored-by: Matthew Cannon <[email protected]> Co-authored-by: Adam Coffman <[email protected]> * Automated frontend build * Automated frontend build * Dev (#487) * switch multiple drug matching to use left anchored, case insensitive search * Update citations with V5 citation (#477) * update citations for v5 * fixed link * prettier * rudimentary support for querying current data_version * implement ga4gh service service info spec * update griffith lab affiliation * run prettier * fix: categories.tsv was downloading interactions (#475) * fix: categories.tsv was downloading interactions * feat: change top row of data downloads to latest * feat: change top row of data downloads to latest * feat: add support for pasting terms into the search bar (#486) * feat: add support for pasting terms into the search bar * feat: allow user to paste searches * refactor: moving demo lists to use function for prettier code * feat: add option for users to paste searches * feat: add option for users to paste searches * feat: persist previous search terms when a user pastes + remove any duplicate terms * feat: update naming for delimiter options * feat: let user paste search terms without selecting bulk search (default to csv), update warning message * feat: add tooltip for bulk search option * update styling * feat: remove paste alert if user clears search terms * feat: make tooltip arrow for better accessibility * feat: justify bulk search option to right (below search buttons) --------- Co-authored-by: Adam Coffman <[email protected]> Co-authored-by: Matthew Cannon <[email protected]> Co-authored-by: Adam Coffman <[email protected]> * Automated frontend build * Automated frontend build --------- Co-authored-by: Adam Coffman <[email protected]> Co-authored-by: Matthew Cannon <[email protected]> Co-authored-by: Adam Coffman <[email protected]> Co-authored-by: katiestahl <[email protected]>
1 parent 6715371 commit 4d9303b

File tree

6 files changed

+175
-34
lines changed

6 files changed

+175
-34
lines changed
 

‎client/src/components/Shared/SearchBar/SearchBar.tsx

Lines changed: 167 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import './SearchBar.scss';
22
import Autocomplete from '@mui/material/Autocomplete';
33
import {
4+
Alert,
5+
AlertTitle,
46
Box,
57
Button,
8+
Checkbox,
9+
FormControl,
10+
FormControlLabel,
11+
InputLabel,
612
MenuItem,
713
Select,
814
SelectChangeEvent,
915
TextField,
16+
Tooltip,
1017
} from '@mui/material';
1118
import { useContext, useEffect } from 'react';
1219
import React from 'react';
@@ -15,6 +22,13 @@ import { ActionTypes } from 'stores/Global/reducers';
1522
import { useGetNameSuggestions } from 'hooks/queries/useGetNameSuggestions';
1623
import { SearchTypes } from 'types/types';
1724
import { useGetIsMobile } from 'hooks/shared/useGetIsMobile';
25+
import HelpIcon from '@mui/icons-material/Help';
26+
27+
enum DelimiterTypes {
28+
Comma = 'Comma',
29+
CommaSpace = 'Comma With Space',
30+
TabNewline = 'Tab or Newline',
31+
}
1832

1933
type SearchBarProps = {
2034
handleSubmit: () => void;
@@ -27,6 +41,10 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
2741
state.interactionMode
2842
);
2943
const [typedSearchTerm, setTypedSearchTerm] = React.useState('');
44+
const [pastingFromDocument, setPastingFromDocument] = React.useState(false);
45+
const [pastedSearchDelimiter, setPastedSearchDelimiter] = React.useState('');
46+
const [searchWasPasted, setSearchWasPasted] = React.useState(false);
47+
3048
const typeAheadQuery = useGetNameSuggestions(typedSearchTerm, searchType);
3149
let autocompleteOptions = typeAheadQuery?.data?.geneSuggestions || [];
3250
const drugAutocompleteOptions = typeAheadQuery?.data?.drugSuggestions || [];
@@ -37,9 +55,11 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
3755

3856
// support searching for terms that the API may not return (add user's typed term to options if it's not already there)
3957
if (
58+
typedSearchTerm &&
4059
autocompleteOptions.filter(
4160
(option: { suggestion: string }) => option.suggestion === typedSearchTerm
42-
).length === 0
61+
).length === 0 &&
62+
typedSearchTerm.trim() !== ''
4363
) {
4464
autocompleteOptions = [
4565
{ suggestion: typedSearchTerm },
@@ -52,6 +72,8 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
5272
const handleAutocompleteChange = (event: any, value: any) => {
5373
if (value.length === 0) {
5474
setSelectedOptions([]);
75+
// for clearing the paste warning, if applicable
76+
setSearchWasPasted(false);
5577
dispatch({ type: ActionTypes.DeleteAllTerms });
5678
} else {
5779
setSelectedOptions(value);
@@ -76,33 +98,36 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
7698

7799
const handleDemoClick = () => {
78100
if (searchType === SearchTypes.Gene) {
79-
setSelectedOptions([
80-
{ suggestion: 'FLT1' },
81-
{ suggestion: 'FLT2' },
82-
{ suggestion: 'FLT3' },
83-
{ suggestion: 'STK1' },
84-
{ suggestion: 'MM1' },
85-
{ suggestion: 'AQP1' },
86-
{ suggestion: 'LOC100508755' },
87-
{ suggestion: 'FAKE1' },
88-
]);
101+
const geneDemoList = [
102+
'FLT1',
103+
'FLT2',
104+
'FLT3',
105+
'STK1',
106+
'MM1',
107+
'AQP1',
108+
'LOC100508755',
109+
'FAKE1',
110+
];
111+
setSelectedOptions(convertToDropdownOptions(geneDemoList));
89112
} else if (searchType === SearchTypes.Drug) {
90-
setSelectedOptions([
91-
{ suggestion: 'SUNITINIB' },
92-
{ suggestion: 'ZALCITABINE' },
93-
{ suggestion: 'TRASTUZUMAB' },
94-
{ suggestion: 'NOTREAL' },
95-
]);
113+
const drugDemoList = [
114+
'SUNITINIB',
115+
'ZALCITABINE',
116+
'TRASTUZUMAB',
117+
'NOTREAL',
118+
];
119+
setSelectedOptions(convertToDropdownOptions(drugDemoList));
96120
} else if (searchType === SearchTypes.Categories) {
97-
setSelectedOptions([
98-
{ suggestion: 'HER2' },
99-
{ suggestion: 'ERBB2' },
100-
{ suggestion: 'PTGDR' },
101-
{ suggestion: 'EGFR' },
102-
{ suggestion: 'RECK' },
103-
{ suggestion: 'KCNMA1' },
104-
{ suggestion: 'MM1' },
105-
]);
121+
const categoriesDemoList = [
122+
'HER2',
123+
'ERBB2',
124+
'PTGDR',
125+
'EGFR',
126+
'RECK',
127+
'KCNMA1',
128+
'MM1',
129+
];
130+
setSelectedOptions(convertToDropdownOptions(categoriesDemoList));
106131
}
107132
};
108133
const handleSearchClick = () => {
@@ -125,9 +150,77 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
125150
}
126151
}, [selectedOptions]);
127152

153+
const convertToDropdownOptions = (options: string[]) => {
154+
return options.map((item: string) => {
155+
return { suggestion: item.trim() };
156+
});
157+
};
158+
159+
const handlePaste = (event: any) => {
160+
let pastedText = event.clipboardData.getData('text');
161+
let pastedOptions: any[] = convertToDropdownOptions([pastedText]);
162+
163+
const commaSepOptions = pastedText.split(',');
164+
165+
if (pastedSearchDelimiter === DelimiterTypes.Comma) {
166+
pastedOptions = convertToDropdownOptions(commaSepOptions);
167+
} else if (pastedSearchDelimiter === DelimiterTypes.CommaSpace) {
168+
const commaSpaceSepOptions = pastedText.split(', ');
169+
pastedOptions = convertToDropdownOptions(commaSpaceSepOptions);
170+
} else if (pastedSearchDelimiter === DelimiterTypes.TabNewline) {
171+
const whitespaceRegex = /[\t\n\r\f\v]/;
172+
const whitespaceSepOptions = pastedText.split(whitespaceRegex);
173+
pastedOptions = convertToDropdownOptions(whitespaceSepOptions);
174+
} else {
175+
pastedOptions = convertToDropdownOptions(commaSepOptions);
176+
}
177+
setSearchWasPasted(true);
178+
// make sure we persist the search terms already entered, combine any pre-existing search terms with the new pasted options
179+
const newSearchOptions = selectedOptions.concat(pastedOptions);
180+
// 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)
181+
const uniqueSearchTerms = [
182+
...new Set(newSearchOptions.map((option) => option.suggestion)),
183+
];
184+
setSelectedOptions(convertToDropdownOptions(uniqueSearchTerms));
185+
// we don't want the code to also run what's in onInputChange for the Autocomplete since everything is handled here
186+
event.preventDefault();
187+
};
188+
189+
const handleCheckboxSelect = (event: any) => {
190+
setPastingFromDocument(event.target.checked);
191+
// reset the selected delimiter and searchWasPasted, to avoid potential weird behaviors if a user deselects the checkbox
192+
setPastedSearchDelimiter('');
193+
setSearchWasPasted(false);
194+
};
195+
196+
const handleDelimiterChange = (event: any) => {
197+
setPastedSearchDelimiter(event.target.value as string);
198+
};
199+
200+
const pasteAlert =
201+
searchWasPasted && pastedSearchDelimiter === '' ? (
202+
<Box width="100%" pb={2}>
203+
<Alert severity="info">
204+
<AlertTitle>Verify your search terms</AlertTitle>
205+
<p>
206+
It looks like you pasted search terms. We have defaulted the
207+
delimiter to comma-separated terms.
208+
</p>
209+
<p style={{ marginTop: '10px' }}>
210+
If this is incorrect or you would like to use a different delimiter,
211+
make sure to check the “Bulk search” option below and select a
212+
delimiter from the drop down.
213+
</p>
214+
</Alert>
215+
</Box>
216+
) : (
217+
<></>
218+
);
219+
128220
return (
129221
<>
130222
<Box id="search-bar-container" width={isMobile ? '95%' : '75%'}>
223+
{pasteAlert}
131224
<Box display="flex" flexWrap={isMobile ? 'wrap' : 'nowrap'}>
132225
<Box width={isMobile ? '100%' : 'fit-content'}>
133226
<Select
@@ -155,6 +248,7 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
155248
<Box>
156249
<TextField
157250
{...params}
251+
onPaste={handlePaste}
158252
variant="standard"
159253
label="Search Terms"
160254
/>
@@ -190,6 +284,53 @@ const SearchBar: React.FC<SearchBarProps> = ({ handleSubmit }) => {
190284
</Button>
191285
</Box>
192286
</Box>
287+
<Box
288+
display="flex"
289+
pt={5}
290+
flexWrap="wrap"
291+
height="100px"
292+
alignContent="center"
293+
justifyContent="end"
294+
>
295+
<Tooltip
296+
title="Select this option if you are pasting terms from an external document"
297+
arrow
298+
>
299+
<FormControlLabel
300+
checked={pastingFromDocument}
301+
onChange={handleCheckboxSelect}
302+
control={<Checkbox />}
303+
label="Bulk search"
304+
/>
305+
</Tooltip>
306+
<Box
307+
width={isMobile ? '100%' : '35%'}
308+
display={pastingFromDocument ? '' : 'none'}
309+
>
310+
<FormControl fullWidth>
311+
<InputLabel id="delimiter-select-label">
312+
Select delimiter
313+
</InputLabel>
314+
<Select
315+
labelId="delimiter-select-label"
316+
id="delimiter-select"
317+
value={pastedSearchDelimiter}
318+
label="Select delimiter"
319+
onChange={handleDelimiterChange}
320+
>
321+
<MenuItem value={DelimiterTypes.Comma}>
322+
{DelimiterTypes.Comma}
323+
</MenuItem>
324+
<MenuItem value={DelimiterTypes.CommaSpace}>
325+
{DelimiterTypes.CommaSpace}
326+
</MenuItem>
327+
<MenuItem value={DelimiterTypes.TabNewline}>
328+
{DelimiterTypes.TabNewline}
329+
</MenuItem>
330+
</Select>
331+
</FormControl>
332+
</Box>
333+
</Box>
193334
</Box>
194335
</>
195336
);

‎server/public/asset-manifest.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"files": {
33
"main.css": "/static/css/main.090445a8.css",
4-
"main.js": "/static/js/main.f12ce273.js",
4+
"main.js": "/static/js/main.c80660af.js",
55
"static/js/461.86214142.chunk.js": "/static/js/461.86214142.chunk.js",
66
"static/js/80.a852d8b5.chunk.js": "/static/js/80.a852d8b5.chunk.js",
77
"static/js/990.d59bee53.chunk.js": "/static/js/990.d59bee53.chunk.js",
@@ -28,7 +28,7 @@
2828
"static/js/925.a41f4405.chunk.js": "/static/js/925.a41f4405.chunk.js",
2929
"index.html": "/index.html",
3030
"main.090445a8.css.map": "/static/css/main.090445a8.css.map",
31-
"main.f12ce273.js.map": "/static/js/main.f12ce273.js.map",
31+
"main.c80660af.js.map": "/static/js/main.c80660af.js.map",
3232
"461.86214142.chunk.js.map": "/static/js/461.86214142.chunk.js.map",
3333
"80.a852d8b5.chunk.js.map": "/static/js/80.a852d8b5.chunk.js.map",
3434
"990.d59bee53.chunk.js.map": "/static/js/990.d59bee53.chunk.js.map",
@@ -56,6 +56,6 @@
5656
},
5757
"entrypoints": [
5858
"static/css/main.090445a8.css",
59-
"static/js/main.f12ce273.js"
59+
"static/js/main.c80660af.js"
6060
]
6161
}

‎server/public/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="DGIdb, The Drug Gene Interaction Database, is a research resource that can be used to search candidate genes or drugs against the known and potentially druggable genome."/><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@300;350;400;500;700;800&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Lato:wght@100;300;350;400;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@200;300;350;400;500;600&display=swap" rel="stylesheet"><link rel="apple-touch-icon" href="/dgidb-icon_48.png"/><link rel="manifest" href="/manifest.json"/><title>DGIdb</title><script defer="defer" src="/static/js/main.f12ce273.js"></script><link href="/static/css/main.090445a8.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="DGIdb, The Drug Gene Interaction Database, is a research resource that can be used to search candidate genes or drugs against the known and potentially druggable genome."/><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@300;350;400;500;700;800&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Lato:wght@100;300;350;400;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@200;300;350;400;500;600&display=swap" rel="stylesheet"><link rel="apple-touch-icon" href="/dgidb-icon_48.png"/><link rel="manifest" href="/manifest.json"/><title>DGIdb</title><script defer="defer" src="/static/js/main.c80660af.js"></script><link href="/static/css/main.090445a8.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

‎server/public/static/js/main.f12ce273.js renamed to ‎server/public/static/js/main.c80660af.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎server/public/static/js/main.f12ce273.js.map renamed to ‎server/public/static/js/main.c80660af.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)