21128: redesign of action sets Arvados-DCO-1.1-Signed-off-by: Lisa Knox <lisa.knox...
[arvados-workbench2.git] / src / components / search-input / search-input.tsx
index edc82d55a419caa55744f39a4e184c66f55fcffc..fbb4f599b60690a5d5e7723f2a1301f25a513487 100644 (file)
@@ -2,12 +2,21 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import * as React from 'react';
-import { IconButton, Paper, StyleRulesCallback, withStyles, WithStyles, FormControl, InputLabel, Input, InputAdornment, FormHelperText } from '@material-ui/core';
+import React, {useState, useEffect} from 'react';
+import {
+    IconButton,
+    FormControl,
+    InputLabel,
+    Input,
+    InputAdornment,
+    Tooltip,
+} from '@material-ui/core';
 import SearchIcon from '@material-ui/icons/Search';
 
 interface SearchInputDataProps {
     value: string;
+    label?: string;
+    selfClearProp: string;
 }
 
 interface SearchInputActionProps {
@@ -15,99 +24,75 @@ interface SearchInputActionProps {
     debounce?: number;
 }
 
-type SearchInputProps = SearchInputDataProps & SearchInputActionProps & WithStyles<CssRules>;
-
-interface SearchInputState {
-    value: string;
-}
+type SearchInputProps = SearchInputDataProps & SearchInputActionProps;
 
 export const DEFAULT_SEARCH_DEBOUNCE = 1000;
 
-class SearchInput extends React.Component<SearchInputProps> {
+export const SearchInput = (props: SearchInputProps) => {
+    const [timeout, setTimeout] = useState(0);
+    const [value, setValue] = useState("");
+    const [label, setLabel] = useState("Search");
+    const [selfClearProp, setSelfClearProp] = useState("");
 
-    state: SearchInputState = {
-        value: ""
-    };
-
-    timeout: number;
-
-    render() {
-        const { classes } = this.props;
-        return <form onSubmit={this.handleSubmit}>
-            <FormControl>
-                <InputLabel>Search</InputLabel>
-                <Input
-                    type="text"
-                    value={this.state.value}
-                    onChange={this.handleChange}
-                    endAdornment={
-                        <InputAdornment position="end">
-                            <IconButton
-                                onClick={this.handleSubmit}>
-                                <SearchIcon />
-                            </IconButton>
-                        </InputAdornment>
-                    } />
-            </FormControl>
-        </form>;
-    }
-
-    componentDidMount() {
-        this.setState({ value: this.props.value });
-    }
-
-    componentWillReceiveProps(nextProps: SearchInputProps) {
-        if (nextProps.value !== this.props.value) {
-            this.setState({ value: nextProps.value });
+    useEffect(() => {
+        if (props.value) {
+            setValue(props.value);
+        }
+        if (props.label) {
+            setLabel(props.label);
         }
-    }
 
-    componentWillUnmount() {
-        clearTimeout(this.timeout);
-    }
+        return () => {
+            setValue("");
+            clearTimeout(timeout);
+        };
+    }, [props.value, props.label]); // eslint-disable-line react-hooks/exhaustive-deps
+
+    useEffect(() => {
+        if (selfClearProp !== props.selfClearProp) {
+            setValue("");
+            setSelfClearProp(props.selfClearProp);
+            handleChange({ target: { value: "" } } as any);
+        }
+    }, [props.selfClearProp]); // eslint-disable-line react-hooks/exhaustive-deps
 
-    handleSubmit = (event: React.FormEvent<HTMLElement>) => {
+    const handleSubmit = (event: React.FormEvent<HTMLElement>) => {
         event.preventDefault();
-        clearTimeout(this.timeout);
-        this.props.onSearch(this.state.value);
-    }
-
-    handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
-        clearTimeout(this.timeout);
-        this.setState({ value: event.target.value });
-        this.timeout = window.setTimeout(
-            () => this.props.onSearch(this.state.value),
-            this.props.debounce || DEFAULT_SEARCH_DEBOUNCE
-        );
-
-    }
-
-}
-
-type CssRules = 'container' | 'input' | 'button';
+        clearTimeout(timeout);
+        props.onSearch(value);
+    };
 
-const styles: StyleRulesCallback<CssRules> = theme => {
-    return {
-        container: {
-            position: 'relative',
-            width: '100%'
-        },
-        input: {
-            border: 'none',
-            borderRadius: theme.spacing.unit / 4,
-            boxSizing: 'border-box',
-            padding: theme.spacing.unit,
-            paddingRight: theme.spacing.unit * 4,
-            width: '100%',
-        },
-        button: {
-            position: 'absolute',
-            top: theme.spacing.unit / 2,
-            right: theme.spacing.unit / 2,
-            width: theme.spacing.unit * 3,
-            height: theme.spacing.unit * 3
-        }
+    const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+        const { target: { value: eventValue } } = event;
+        clearTimeout(timeout);
+        setValue(eventValue);
+
+        setTimeout(window.setTimeout(
+            () => {
+                props.onSearch(eventValue);
+            },
+            props.debounce || DEFAULT_SEARCH_DEBOUNCE
+        ));
     };
-};
 
-export default withStyles(styles)(SearchInput);
\ No newline at end of file
+    return <form onSubmit={handleSubmit}>
+        <FormControl>
+            <InputLabel>{label}</InputLabel>
+            <Input
+                type="text"
+                data-cy="search-input"
+                value={value}
+                onChange={handleChange}
+                endAdornment={
+                    <InputAdornment position="end">
+                        <Tooltip title='Search'>
+                            <IconButton
+                                onClick={handleSubmit}>
+                                <SearchIcon />
+                            </IconButton>
+                        </Tooltip>
+                    </InputAdornment>
+                } />
+        </FormControl>
+    </form>;
+};