department-of-veterans-affairs/vets-website

View on GitHub
src/applications/ask-va/components/search/SearchControls.jsx

Summary

Maintainability
C
1 day
Test Coverage
import { focusElement } from 'platform/utilities/ui';
import React, { useEffect, useState } from 'react';
import { connect, useDispatch } from 'react-redux';

import { geoLocateUser } from '../../actions/geoLocateUser';
import { setLocationInput } from '../../actions/index';
import { convertLocation } from '../../utils/mapbox';

const SearchControls = props => {
  const {
    onSubmit,
    locateUser,
    geolocationInProgress,
    userLocation,
    searchQuery,
    geoCodeError,
    searchTitle,
    searchHint,
    hasSearchInput,
  } = props;

  const [queryState, setQueryState] = useState(searchQuery);
  const onlySpaces = str => /^\s+$/.test(str);
  const dispatch = useDispatch();

  const handleQueryChange = e => {
    const q = e.target.value;

    // Prevent users from entering spaces because this will not trigger a change when they exit the field
    setQueryState(onlySpaces(q) ? q.trim() : q);
  };

  const handleSubmit = e => {
    e.preventDefault();
    dispatch(setLocationInput(queryState));
    onSubmit(queryState);
  };

  const handleGeolocationButtonClick = async e => {
    e.preventDefault();
    dispatch(geoLocateUser());
  };

  useEffect(
    () => {
      const getLocation = async () => {
        if (userLocation && !geoCodeError) {
          const place = await convertLocation(userLocation);
          setQueryState(place.place_name);
          dispatch(setLocationInput(place));
          await locateUser(userLocation);
        }
      };
      getLocation();
    },
    [userLocation],
  );

  const handleClearInput = () => {
    setQueryState('');
    focusElement('#street-city-state-zip');
  };

  const renderLocationInputField = () => {
    return (
      <div>
        <div id="location-input-field">
          <label
            htmlFor="street-city-state-zip"
            id="street-city-state-zip-label"
          >
            {searchTitle}
            <span className="form-required-span">(*Required)</span>
          </label>
          {geolocationInProgress ? (
            <div className="use-my-location-link">
              <va-loading-indicator
                label="Finding your location"
                message="Finding your location..."
                set-focus
              />
            </div>
          ) : (
            <button
              onClick={handleGeolocationButtonClick}
              type="button"
              className="use-my-location-link vads-u-display--flex vads-u-align-items--center"
            >
              <va-icon icon="near_me" size={3} />
              Use my location
            </button>
          )}
        </div>
        {(geoCodeError || hasSearchInput) && (
          <span className="usa-input-error-message" role="alert">
            <span className="sr-only">Error</span>
            Please fill in a city or postal code.
          </span>
        )}
        {searchHint && <p className="search-hint-text">{searchHint}</p>}
        <div className="search-input">
          <input
            className="usa-input"
            id="street-city-state-zip"
            name="street-city-state-zip"
            type="search"
            onChange={handleQueryChange}
            value={queryState}
            title="Your location: Street, City, State or Postal code"
          />
          {queryState?.length > 0 && (
            <button
              aria-label="Clear city or postal code"
              type="button"
              id="clear-input"
              className="clear-button"
              onClick={handleClearInput}
            >
              <va-icon
                icon="cancel"
                size={2}
                id="clear-input"
                onClick={handleClearInput}
              />
            </button>
          )}
          <button type="button" id="facility-search" onClick={handleSubmit}>
            <span className="button-text">Search</span>
            <span className="button-icon">
              <va-icon icon="search" size={3} />
            </span>
          </button>
        </div>
      </div>
    );
  };

  return (
    <div className="search-controls-container clearfix">
      <div id="facility-search-controls">
        <div className="columns vads-u-padding-0">
          {renderLocationInputField()}
        </div>
      </div>
    </div>
  );
};

function mapStateToProps(state) {
  return {
    userLocation: state.askVA.currentUserLocation,
    searchQuery: state.askVA.searchLocationInput,
    geolocationInProgress: state.askVA.getLocationInProgress,
    geoCodeError: state.askVA.getLocationError,
  };
}

export default connect(mapStateToProps)(SearchControls);