22083: Store "failedToLoadOutputCollection" state
[arvados.git] / services / workbench2 / src / components / search-input / search-input.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React, {useState, useEffect} from 'react';
6 import {
7     IconButton,
8     FormControl,
9     InputLabel,
10     Input,
11     InputAdornment,
12     Tooltip,
13 } from '@mui/material';
14 import SearchIcon from '@mui/icons-material/Search';
15
16 interface SearchInputDataProps {
17     value: string;
18     label?: string;
19     selfClearProp: string;
20     width?: string;
21 }
22
23 interface SearchInputActionProps {
24     onSearch: (value: string) => any;
25     debounce?: number;
26 }
27
28 type SearchInputProps = SearchInputDataProps & SearchInputActionProps;
29
30 export const DEFAULT_SEARCH_DEBOUNCE = 1000;
31
32 export const SearchInput = (props: SearchInputProps) => {
33     const [timeout, setTimeout] = useState(0);
34     const [value, setValue] = useState("");
35     const [label, setLabel] = useState("Search");
36     const [selfClearProp, setSelfClearProp] = useState("");
37
38     useEffect(() => {
39         if (props.value) {
40             setValue(props.value);
41         }
42         if (props.label) {
43             setLabel(props.label);
44         }
45
46         return () => {
47             setValue("");
48             clearTimeout(timeout);
49         };
50         // eslint-disable-next-line react-hooks/exhaustive-deps
51     }, [props.value, props.label]);
52
53     useEffect(() => {
54         if (selfClearProp !== props.selfClearProp) {
55             setValue("");
56             setSelfClearProp(props.selfClearProp);
57             handleChange({ target: { value: "" } } as any);
58         }
59         // eslint-disable-next-line react-hooks/exhaustive-deps
60     }, [props.selfClearProp]);
61
62     const handleSubmit = (event: React.FormEvent<HTMLElement>) => {
63         event.preventDefault();
64         clearTimeout(timeout);
65         props.onSearch(value);
66     };
67
68     const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
69         const { target: { value: eventValue } } = event;
70         clearTimeout(timeout);
71         setValue(eventValue);
72
73         setTimeout(window.setTimeout(
74             () => {
75                 props.onSearch(eventValue);
76             },
77             props.debounce || DEFAULT_SEARCH_DEBOUNCE
78         ));
79     };
80
81     return (
82         <form onSubmit={handleSubmit}>
83             <FormControl variant="standard" style={{ width: props.width || '14rem'}}>
84                 <InputLabel>{label}</InputLabel>
85                 <Input
86                     type="text"
87                     data-cy="search-input"
88                     value={value}
89                     onChange={handleChange}
90                     endAdornment={
91                         <InputAdornment position="end" style={{marginRight: '-0.6rem'}}>
92                             <Tooltip title='Search'>
93                                 <IconButton onClick={handleSubmit} size="large">
94                                     <SearchIcon />
95                                 </IconButton>
96                             </Tooltip>
97                         </InputAdornment>
98                     } />
99             </FormControl>
100         </form>
101     );
102 };