Skip to content

Commit

Permalink
Author autocomplete (front-end)
Browse files Browse the repository at this point in the history
  • Loading branch information
RC-Lee committed Apr 7, 2022
1 parent 9d376e8 commit 65c2e36
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 23 deletions.
119 changes: 119 additions & 0 deletions src/web/app/src/components/SearchInput/AuthorInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { TextField } from '@material-ui/core';
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
import { Autocomplete } from '@mui/material';
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { searchServiceUrl } from '../../config';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
input: {
fontSize: '1.2rem',
color: theme.palette.text.primary,
borderRadius: `4rem`,
borderColor: theme.palette.info.main,
borderWidth: `2px`,
},
listbox: {
fontSize: '1.2rem',
color: theme.palette.text.primary,
backgroundColor: theme.palette.background.paper,
'& :hover': {
color: theme.palette.text.secondary,
backgroundColor: theme.palette.background.default,
border: '2px solid',
},
},
})
);

const fetchResults = async (url: string | null) => {
if (url) {
try {
const res = await fetch(url);
if (res.ok) {
const results = await res.json();
// return up to first 10 results
return results.res.slice(0, 10);
}
} catch (error) {
console.error(error);
return [];
}
}
return [];
};

interface AuthorInputInterface {
text: string;
setText: Dispatch<SetStateAction<string>>;
labelFor: string;
}

const AuthorInput = ({ text, setText, labelFor }: AuthorInputInterface) => {
const cs = useStyles();
const [options, setOptions] = useState<[{ author: string; highlight: string }]>();

useEffect(() => {
// debounce so it searches every 0.5 seconds, instead of on every stroke
const debounce = setTimeout(() => {
(async () => {
const prepareUrl = () => `${searchServiceUrl}/authors/autocomplete/?author=${text}`;
// Do the request if there is something to search for
const shouldFetch = () => text.length > 0;
const data = await fetchResults(shouldFetch() ? prepareUrl() : null);
if (data) {
setOptions(data);
}
})();
}, 500);

return () => clearTimeout(debounce);
}, [text]);

// mui Autocomplete component https://mui.com/components/autocomplete/
return (
<Autocomplete
id="author-autocomplete"
freeSolo
getOptionLabel={(option) => (typeof option === 'string' ? option : option.author)}
options={options || []}
// disable built-in filtering for search as you type
// https://mui.com/components/autocomplete/#search-as-you-type
filterOptions={(x) => x}
value={text}
onInputChange={(_event, newInputValue) => {
setText(newInputValue);
}}
fullWidth
classes={{
listbox: cs.listbox,
}}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
size="medium"
fullWidth
label={labelFor}
InputProps={{
...params.InputProps,
type: 'search',
classes: {
root: cs.input,
focused: cs.input,
notchedOutline: cs.input,
},
}}
InputLabelProps={{
classes: {
root: cs.input,
focused: cs.input,
},
}}
/>
)}
/>
);
};

export default AuthorInput;
51 changes: 28 additions & 23 deletions src/web/app/src/components/SearchInput/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Dispatch, SetStateAction } from 'react';
import { makeStyles, createStyles, Theme } from '@material-ui/core/styles';
import { IconButton, TextField } from '@material-ui/core';
import SearchIcon from '@material-ui/icons/Search';
import AuthorInput from './AuthorInput';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
Expand All @@ -12,13 +13,13 @@ const useStyles = makeStyles((theme: Theme) =>
marginTop: '1rem',
},
customInput: {
borderRadius: `4rem 4rem 4rem 4rem`,
borderRadius: `4rem`,
borderColor: theme.palette.info.main,
borderWidth: `2px`,
transition: theme.transitions.create(['background-color', 'border-color'], {
duration: '.5s',
}),
fontSize: '14px',
fontSize: '1.2rem',
display: 'block',
color: theme.palette.text.primary,
},
Expand Down Expand Up @@ -61,27 +62,31 @@ const SearchInput = ({ text, setText, labelFor, clickEvent }: SearchInputInterfa

return (
<div className={classes.wrapper}>
<TextField
variant="outlined"
size="medium"
fullWidth
label={labelFor}
onChange={(event) => setText(event.target.value)}
value={text}
InputProps={{
classes: {
root: classes.customInput,
focused: classes.customInput,
notchedOutline: classes.customInput,
},
}}
InputLabelProps={{
classes: {
root: classes.customInputText,
focused: classes.customInputText,
},
}}
/>
{labelFor === 'Look for an Author' ? (
<AuthorInput text={text} setText={setText} labelFor={labelFor} />
) : (
<TextField
variant="outlined"
size="medium"
fullWidth
label={labelFor}
onChange={(event) => setText(event.target.value)}
value={text}
InputProps={{
classes: {
root: classes.customInput,
focused: classes.customInput,
notchedOutline: classes.customInput,
},
}}
InputLabelProps={{
classes: {
root: classes.customInputText,
focused: classes.customInputText,
},
}}
/>
)}
{clickEvent && (
<IconButton
className={classes.iconButton}
Expand Down

0 comments on commit 65c2e36

Please sign in to comment.