Autocomplete
Thu May 27 2021 15:31:56 GMT+0000 (Coordinated Universal Time)
import React, { useState, useEffect } from 'react' import { connect } from 'react-redux' import { TextField, List, ListItem, CircularProgress, InputAdornment, createMuiTheme, makeStyles } from '@material-ui/core' import NodeSuggestions from 'components/Autocomplete/NodeSuggestions' import AddressSuggestions from 'components/Autocomplete/AddressSuggestions' import UserSuggestions from 'components/Autocomplete/UserSuggestions' import GeocodedSuggestion from 'components/Autocomplete/GeocodedSuggestion' import {map} from 'lodash' import { ARROW_UP, ARROW_DOWN, enter, onInputChange, RESET_AUTOCOMPLETE, contextToData } from 'containers/Autocomplete/actions' import PickUp from 'components/Icons/PickUp/PickUp.jsx' import {useIntl} from 'react-intl' const suggestionProviders = { 'geocodes': GeocodedSuggestion, 'nodes': NodeSuggestions, 'addresses': AddressSuggestions, 'users': UserSuggestions } const useStyles = makeStyles((theme) => ({ root: { '& .MuiTextField-root': { margin: '20px', } } })) function Autocomplete(props){ const classes = useStyles() /******** props from parent * text : the text in the field, controlled by parent with: * onChange * context : the view where is the autocomplete => which data it displays ? * onSelect : when clicking on suggestion */ const { onChange, onInputChange, context, onSelect, loading, highlightedIndex } = props const { text, helperText, label} = _.get(props, 'inputProps', {}) /******* props generated from inside */ const { arrowUp, arrowDown, enter, resetAutocomplete } = props /******** internal state that is not needed above the component */ const [isFocused, setFocus] = useState(false) // conditional opening of autocomplete const open = isFocused && text && text.length >= 3 // highlighted index (for keyboard navigation) is managed via redux to answer the case of // switching from node to address suggestion const handleKey = (e) => { e.stopPropagation() switch (e.keyCode) { // second thought : a "selected" element with arrows is also displayed and entered in form // context to know which datatypes are displayed and to guess the highlightedIndex element case 38:// arrowUp key arrowUp(context) enter(context, onSelect) break case 40:// arrowDown key arrowDown(context) enter(context, onSelect) break case 13:// enter key enter(context, onSelect) setFocus(false) resetAutocomplete() break } } // below is cleaning the "highlightedIndex" in an edge case useEffect(()=>{ if(isFocused && text && text.length < 3){ resetAutocomplete() } },[text]) const intl = useIntl() return( <div className="fullWidth" style={{position:'relative'}} //ACCESSIBILITY role="combobox" aria-expanded={open} > <label htmlFor={label} style={{color:'#6c6c6c'}}>{label}</label> <TextField autoComplete="off" // not from the browser id={label} fullWidth value={text} onChange={e =>{ // update search.formData and textfield onChange(e) // update autoComplete only when user types a letter onInputChange(e.target.value, context) setFocus(true) }} onFocus={()=>{ // loads suggestions also when clicking if (text && text.length >= 3){ onInputChange(text, context) } setFocus(true) }} onBlur={()=>{ setFocus(false) resetAutocomplete() }} onKeyDown={handleKey} // label={label} // hiddenLabel helperText={helperText} error={Boolean(helperText)} title={label} InputProps={{ disableUnderline: true }} InputLabelProps={{ shrink: true }} // ACCESSIBILITY inputProps={{ autoComplete:'off', 'aria-autocomplete':open ? 'list' : 'none', 'aria-controls':open ? 'autocomplete_results' : '', 'aria-activedescendant': open ? 'suggestion-'+highlightedIndex : '', role:'textbox' }} placeholder={intl.formatMessage({ id: 'help.address' })} style={{height: '2.5rem', marginTop:'0.5rem', backgroundColor:'#f1f0f0', borderRadius:'0 5px 5px 0'}} /> { open && <List tabIndex={-1} disablePadding className="suggestionsContainer" // ACCESSIBILITY id="autocomplete_results" role="listbox" > { loading ? <div className="row centered fullWidth padded"><CircularProgress size={20} /></div> : map(contextToData[context],(dataType,i)=>{ const SuggestionProvider = suggestionProviders[dataType] return( <SuggestionProvider key={i} onSelect={onSelect} /> ) })} </List> } </div> ) } const mapStateToProps = state => ({ loading: state.autocomplete.loading, highlightedIndex: state.autocomplete.highlightedIndex }) const mapDispatchToProps = dispatch => ({ arrowUp : (context) => dispatch({ type : ARROW_UP, context }), arrowDown : (context) => dispatch({ type : ARROW_DOWN, context }), enter : (context, onSelect) => dispatch(enter(context, onSelect)), onInputChange : (newText, context) => dispatch(onInputChange(newText, context)), resetAutocomplete: () => dispatch({type: RESET_AUTOCOMPLETE}) }) export default connect(mapStateToProps, mapDispatchToProps)(Autocomplete)
Comments