SearchBar.tsx 4.55 KB
Newer Older
1
import * as React from 'react';
Andrew Hrdy's avatar
Andrew Hrdy committed
2
import * as classNames from 'classnames';
3
import { useDispatch } from 'react-redux';
4
import { CampusRegion } from '../models/facility.model';
5
import { setSearchTermAction, setSelectedCampusRegionAction } from '../store/ui/ui.actions';
Andrew Hrdy's avatar
Andrew Hrdy committed
6
import { trackPiwikEvent } from '../piwik/piwik';
7
import { IconButton, Input, Paper, MenuItem, Select, FormControl } from '@material-ui/core';
8
9
10
import SearchIcon from '@material-ui/icons/Search';
import CloseIcon from '@material-ui/icons/Close';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

interface SearchBarProps {
    onSearchExpand: () => void;
    onSearchCollapse: () => void;
}

export default ({onSearchExpand, onSearchCollapse}: SearchBarProps) => {
    const [isFocused, setIsFocused] = React.useState<boolean>(false);
    const [isMobileOpen, setIsMobileOpen] = React.useState<boolean>(false);
    const [value, setValue] = React.useState<string>('');
    const [campus, setCampus] = React.useState<CampusRegion>(CampusRegion.Fairfax);
    const [inputElement, setInputElement] = React.useState<HTMLInputElement>(null);

    const dispatch = useDispatch();
    const setSearchTerm = (term: string) => dispatch(setSearchTermAction(term));
    const setSelectedCampusRegion = (region: CampusRegion) => dispatch(setSelectedCampusRegionAction(region));
Andrew Hrdy's avatar
Andrew Hrdy committed
27

Andrew Hrdy's avatar
Andrew Hrdy committed
28
29
30
    /**
     * Handles a change in the search term.
     */
31
32
    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setValue(e.target.value);
Andrew Hrdy's avatar
Andrew Hrdy committed
33

34
35
        setSearchTerm(e.target.value);
    };
Andrew Hrdy's avatar
Andrew Hrdy committed
36

37
    const handleRegionChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
Andrew Hrdy's avatar
Andrew Hrdy committed
38
        trackPiwikEvent('change-campus', e.target.value);
39

40
        const campusRegion: CampusRegion = e.target.value as CampusRegion;
41
        setCampus(campusRegion);
Andrew Hrdy's avatar
Andrew Hrdy committed
42

43
44
        setSelectedCampusRegion(campusRegion);
    };
45

46
    const handleFocus = () => {
Andrew Hrdy's avatar
Andrew Hrdy committed
47
        trackPiwikEvent('search-action', 'focused');
48

49
50
        setIsFocused(true);
    };
Andrew Hrdy's avatar
Andrew Hrdy committed
51

52
53
    const handleBlur = () => setIsFocused(false);
    
Andrew Hrdy's avatar
Andrew Hrdy committed
54

55
56
    const handleMobileExpand = () => {
        setIsMobileOpen(true);
Andrew Hrdy's avatar
Andrew Hrdy committed
57

58
        onSearchExpand();
Andrew Hrdy's avatar
Andrew Hrdy committed
59

Andrew Hrdy's avatar
Andrew Hrdy committed
60
61
62
63
64
        /*
            This timeout is necessary because inputElement is not displayed when execution reaches this point.
            By using setTimeout with no delay, inputElement.focus() is added to the end of the execution stack, therefore
            being executed after the input is visible.,
         */
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
        setTimeout(() => inputElement?.focus());
    };

    const handleMobileCollapse = () => {
        setIsMobileOpen(false);

        onSearchCollapse();
    };

    const clear = () => {
        setValue('');

        setSearchTerm('');
    };
    
    return (
        <Paper className={classNames('search-bar-paper-background', isFocused && 'search-bar-focus',
82
            value && 'search-bar-has-value', isMobileOpen && 'search-bar-mobile-open')}>
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
            <IconButton onClick={handleMobileExpand} disableRipple={true} className={'search-bar-search-btn'}>
                <SearchIcon className={'search-bar-search-icon'}/>
            </IconButton>
            
            <IconButton onClick={handleMobileCollapse} disableRipple={true} className={'search-bar-back-btn'}>
                <ArrowBackIcon className={'search-bar-back-icon'}/>
            </IconButton>

            <Input
                placeholder="Name, Location, etc."
                disableUnderline={true}
                className={classNames('search-bar-input', {
                    'hide-search-input': !isMobileOpen
                })}
                onChange={handleChange}
                onFocus={handleFocus}
                onBlur={handleBlur}
                inputProps={{
                    'aria-label': 'Search Bar'
                }}
                inputRef={(el) => setInputElement(el)}
                value={value}
            />

            <IconButton onClick={clear} disableRipple={true} className={'search-bar-close-btn'}>
                <CloseIcon/>
            </IconButton>

            <FormControl className={'search-bar-campus-control'}>
                <Select
113
                    disableUnderline={true}
114
115
116
117
118
119
120
121
122
123
124
                    value={campus}
                    onChange={handleRegionChange}>
                    <MenuItem value={CampusRegion.Fairfax}>Fairfax</MenuItem>
                    <MenuItem value={CampusRegion.Arlington}>Arlington</MenuItem>
                    <MenuItem value={CampusRegion.PrinceWilliam}>SciTech</MenuItem>
                    <MenuItem value={CampusRegion.FrontRoyal}>Front Royal</MenuItem>
                </Select>
            </FormControl>
        </Paper>
    );
};