department-of-veterans-affairs/vets-website

View on GitHub
src/applications/ask-va/components/FormFields/AddressValidationRadio.jsx

Summary

Maintainability
C
1 day
Test Coverage
import { VaAlert } from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import { setData } from '@department-of-veterans-affairs/platform-forms-system/actions';
import { apiRequest } from '@department-of-veterans-affairs/platform-utilities/exports';
import { focusElement } from '@department-of-veterans-affairs/platform-utilities/ui';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { URL, envUrl } from '../../constants';
import { formatAddress } from '../../utils/helpers';

const AddressValidationRadio = props => {
  const { formData, setFormData } = props;
  const [apiData, setApiData] = useState([]);
  const [loading, isLoading] = useState(false);

  const [selectedAddress, setSelectedAddress] = useState('');

  const handleValueChange = (address, id) => {
    setSelectedAddress(id);
    const addressString = JSON.stringify({
      city: address.city,
      country: address.countryCodeIso3,
      postalCode: address.zipCode,
      state: address.stateCode,
      province: address.stateProvince?.code || '',
      street: address.addressLine1,
      street2: address.addressLine2 || '',
    });

    setFormData({
      ...formData,
      addressValidation: addressString,
    });
  };

  const {
    state,
    street,
    street2,
    city,
    postalCode,
    militaryAddress,
  } = formData.address;

  const { militaryPostOffice, militaryState } = militaryAddress || {};

  /* eslint-disable camelcase */
  const postData = {
    address_line1: street,
    address_line2: street2,
    city: city || militaryPostOffice,
    zip_code: postalCode,
    state_code: state || militaryState,
    country_name: 'United States',
    country_code_iso3: 'USA',
    address_pou: 'RESIDENCE/CHOICE',
    address_type: 'DOMESTIC',
  };

  const options = {
    body: JSON.stringify({ address: { ...postData } }),
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
  };

  const getApiData = url => {
    isLoading(true);
    return apiRequest(url, options)
      .then(res => {
        setApiData(res.addresses);
        isLoading(false);
      })
      .catch(() => {
        isLoading(false);
        handleValueChange(formData.address, 'userEntered');
      });
  };

  useEffect(() => {
    getApiData(`${envUrl}${URL.ADDRESS_VALIDATION}`);
    focusElement('#address-validation-alert-heading');
  }, []);

  useEffect(
    () => {
      if (apiData.length > 0) {
        handleValueChange(apiData[0].address, '0');
      }
    },
    [apiData],
  );

  const renderAddressOption = (address, id = 'userEntered') => {
    const hasConfirmedSuggestions = apiData.length > 0;
    const { addressStreet, cityStateZip, addressCountry } = formatAddress(
      address,
    );

    return (
      <div key={id} className="vads-u-margin-bottom--1p5">
        {hasConfirmedSuggestions && (
          <input
            key={id}
            type="radio"
            id={id}
            onChange={() => {
              handleValueChange(address, id);
            }}
            checked={selectedAddress === id}
          />
        )}
        <label
          htmlFor={id}
          className="vads-u-margin-top--2 vads-u-display--flex vads-u-align-items--center"
        >
          <div className="vads-u-display--flex vads-u-flex-direction--column vads-u-padding-bottom--0p5">
            <span
              className="dd-privacy-hidden"
              data-dd-action-name="street address"
            >
              {addressStreet}
            </span>
            <span
              className="dd-privacy-hidden"
              data-dd-action-name="city, state and zip code"
            >
              {cityStateZip}
            </span>
            <span>{addressCountry}</span>
          </div>
        </label>
      </div>
    );
  };

  // render loading indicator while we fetch
  if (loading) {
    return (
      <va-loading-indicator label="Loading" message="Loading..." set-focus />
    );
  }

  const deliveryPointValidation =
    apiData &&
    apiData.length > 0 &&
    apiData[0].addressMetaData.deliveryPointValidation;

  const shouldShowSuggestions =
    apiData && apiData.length > 0 && deliveryPointValidation === 'CONFIRMED';

  return (
    <>
      <div role="alert">
        {shouldShowSuggestions && (
          <VaAlert
            className="vads-u-margin-bottom--2 vads-u-margin-top--2 vads-u-padding-bottom--1"
            status="warning"
            visible
            uswds
          >
            <h4
              id="address-validation-alert-heading"
              slot="headline"
              className="vads-u-font-size--h3"
            >
              We can’t confirm the address you entered with the U.S. Postal
              Service
            </h4>
            <p className="vads-u-margin-y--0">
              Tell us which address you’d like to use.
            </p>
          </VaAlert>
        )}
      </div>
      <div>
        <span className="vads-u-font-weight--bold">You entered:</span>
        {renderAddressOption(formData.address)}
        <span className="vads-u-font-weight--bold">
          Suggested {apiData.length > 1 ? 'addresses' : 'address'}:
        </span>
        {shouldShowSuggestions ? (
          apiData.map((item, index) =>
            renderAddressOption(item.address, String(index)),
          )
        ) : (
          <p>No recommendations available</p>
        )}
      </div>
    </>
  );
};

AddressValidationRadio.propTypes = {
  formData: PropTypes.object,
  setFormData: PropTypes.func,
};

const mapStateToProps = state => ({
  formData: state.form?.data || {},
});

const mapDispatchToProps = {
  setFormData: setData,
};

export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(AddressValidationRadio);