@navinc/base-react-components
Version:
Nav's Pattern Library
110 lines (99 loc) • 3.37 kB
JavaScript
import React, { useEffect, useRef } from 'react'
import styled from 'styled-components'
import { Copy } from './copy'
import { Input, Label, FieldWrapper, Field, Errors, Err } from './form-elements/shared.js'
import { loadScript } from '@navinc/utils'
import noop from '@navinc/utils/noop.js'
const handlePreventSubmitOnEnter = (keyDownEvent) => {
if (keyDownEvent.keyCode === 13) keyDownEvent.preventDefault()
}
export const AddressInput = ({
className,
label,
hasSpaceForErrors,
isInvalid,
value,
required,
type,
errors = [],
lede = '',
onAddressSelected = noop,
touched,
placeholder = 'Enter a location',
...props
}) => {
const inputRef = useRef(null)
useEffect(() => {
Promise.resolve(
global?.google?.maps?.places?.Autocomplete ??
loadScript(
'https://maps.googleapis.com/maps/api/js?key=AIzaSyAvfE0mS4qeMdHm2fsIfE2XowJj6XG2pW8&libraries=places'
).then(
() =>
global?.google?.maps?.places?.Autocomplete ?? Promise.reject(new Error('Error loading GoogleMaps script'))
)
)
.then((Autocomplete) => {
const autocomplete = new Autocomplete(inputRef.current, {
types: ['address'],
componentRestrictions: { country: 'us' },
})
autocomplete.addListener('place_changed', () => {
const place = autocomplete.getPlace()
// bail if there is a problem getting data from the api
if (!place || !Array.isArray(place.address_components)) return
const {
locality: city = '',
administrative_area_level_1: state = '',
postal_code: zip = '',
street_number: streetNumber = '',
route: street = '',
street1 = `${streetNumber} ${street}`.trim(),
} = Object.assign(
...place.address_components
.filter(({ types: [type] }) =>
['street_number', 'route', 'locality', 'administrative_area_level_1', 'postal_code'].includes(type)
)
.map(({ types: [type], long_name: longName, short_name: shortName }) => ({
[type]: ['route', 'locality'].includes(type) ? longName : shortName,
}))
)
onAddressSelected({ city, state, street1, zip })
})
})
.catch((err) => console.error(err))
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<FieldWrapper className={className}>
{lede && <Copy bold>{lede}</Copy>}
<Field
className="js-private"
isInvalid={isInvalid}
isVisited={touched || value || placeholder}
onKeyDown={handlePreventSubmitOnEnter}
required={required}
type={type}
value={value}
>
<Input
ref={inputRef}
type={type}
required={required}
value={value}
isInvalid={isInvalid}
placeholder={placeholder}
{...props}
/>
<Label required={required} value={value}>
{label}
</Label>
</Field>
<Errors hasSpaceForErrors={hasSpaceForErrors}>
{!!errors.length && errors.map((err, i) => <Err key={`err-${i}`}>{err}</Err>)}
</Errors>
</FieldWrapper>
)
}
const StyledAddressInput = styled(AddressInput)``
export default StyledAddressInput