Merge branch '17579-Clear-table-filter-when-changing-the-project' into main
[arvados-workbench2.git] / 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 from 'react';
6 import { IconButton, StyleRulesCallback, withStyles, WithStyles, FormControl, InputLabel, Input, InputAdornment, Tooltip } from '@material-ui/core';
7 import SearchIcon from '@material-ui/icons/Search';
8
9 type CssRules = 'container' | 'input' | 'button';
10
11 const styles: StyleRulesCallback<CssRules> = theme => {
12     return {
13         container: {
14             position: 'relative',
15             width: '100%'
16         },
17         input: {
18             border: 'none',
19             borderRadius: theme.spacing.unit / 4,
20             boxSizing: 'border-box',
21             padding: theme.spacing.unit,
22             paddingRight: theme.spacing.unit * 4,
23             width: '100%',
24         },
25         button: {
26             position: 'absolute',
27             top: theme.spacing.unit / 2,
28             right: theme.spacing.unit / 2,
29             width: theme.spacing.unit * 3,
30             height: theme.spacing.unit * 3
31         }
32     };
33 };
34
35 interface SearchInputDataProps {
36     value: string;
37     label?: string;
38     selfClearProp: string;
39 }
40
41 interface SearchInputActionProps {
42     onSearch: (value: string) => any;
43     debounce?: number;
44 }
45
46 type SearchInputProps = SearchInputDataProps & SearchInputActionProps & WithStyles<CssRules>;
47
48 interface SearchInputState {
49     value: string;
50     label: string;
51     selfClearProp: string;
52 }
53
54 export const DEFAULT_SEARCH_DEBOUNCE = 1000;
55
56 export const SearchInput = withStyles(styles)(
57     class extends React.Component<SearchInputProps> {
58         state: SearchInputState = {
59             value: "",
60             label: "",
61             selfClearProp: ""
62         };
63
64         timeout: number;
65
66         render() {
67             return <form onSubmit={this.handleSubmit}>
68                 <FormControl>
69                     <InputLabel>{this.state.label}</InputLabel>
70                     <Input
71                         type="text"
72                         data-cy="search-input"
73                         value={this.state.value}
74                         onChange={this.handleChange}
75                         endAdornment={
76                             <InputAdornment position="end">
77                                 <Tooltip title='Search'>
78                                     <IconButton
79                                         onClick={this.handleSubmit}>
80                                         <SearchIcon />
81                                     </IconButton>
82                                 </Tooltip>
83                             </InputAdornment>
84                         } />
85                 </FormControl>
86             </form>;
87         }
88
89         componentDidMount() {
90             this.setState({
91                 value: this.props.value,
92                 label: this.props.label || 'Search'
93             });
94         }
95
96         componentWillReceiveProps(nextProps: SearchInputProps) {
97             if (nextProps.value !== this.props.value) {
98                 this.setState({ value: nextProps.value });
99             }
100             if (this.state.value !== '' && nextProps.selfClearProp && nextProps.selfClearProp !== this.state.selfClearProp) {
101                 this.props.onSearch('');
102                 this.setState({ selfClearProp: nextProps.selfClearProp });
103             }
104         }
105
106         componentWillUnmount() {
107             clearTimeout(this.timeout);
108         }
109
110         handleSubmit = (event: React.FormEvent<HTMLElement>) => {
111             event.preventDefault();
112             clearTimeout(this.timeout);
113             this.props.onSearch(this.state.value);
114         }
115
116         handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
117             clearTimeout(this.timeout);
118             this.setState({ value: event.target.value });
119             this.timeout = window.setTimeout(
120                 () => this.props.onSearch(this.state.value),
121                 this.props.debounce || DEFAULT_SEARCH_DEBOUNCE
122             );
123
124         }
125     }
126 );