vtex / address-form

A React component that renders VTEX's address forms
GNU Affero General Public License v3.0
27 stars 27 forks source link
srv-checkout-ui xp-shopping

Address Form

A React component that renders VTEX's address forms

Setup

Through NPM:

$ npm install @vtex/address-form
import AddressContainer from '@vtex/address-form/lib/AddressContainer'

Through vtex.io:

Add vtex.address-form to your manifest.json dependencies

import { AddressContainer } from 'vtex.address-form'

Helper functions are properties of the helpers import

import { helpers } from 'vtex.address-form'

const { addValidation } = helpers

API

Base Components

Geolocation Components

Helper Functions

Public modules

Types


Base Components

AddressContainer

This component handles the input validation based on the country rules provided. It also calls the Postal Code service to autocomplete the address fields.

It provides an onChangeAddress() function and the address object to its child components by context. The components will receive such parameters injected to their props. It may also provide an Input parameter via context, representing the input component that the children components should use to display their data.

When a field change its value, it should call the function with an object like so:

onChangeAddress({
  street: { value: 'newValueHere' },
})

You can also call it with more than one field:

onChangeAddress({
  street: { value: 'newValueHere' },
  number: { value: 'newValueHere' },
})

Props

AddressContainer.propTypes = {
  cors: PropTypes.bool,
  accountName: PropTypes.string,
  address: AddressShapeWithValidation,
  rules: PropTypes.object.isRequired,
  Input: PropTypes.func,
  onChangeAddress: PropTypes.func.isRequired,
  children: PropTypes.func.isRequired,
  autoCompletePostalCode: PropTypes.bool,
}

Example

<AddressContainer
  address={address}
  rules={rules}
  onChangeAddress={this.handleAddressChange}
>
  {onChangeAddress =>
    <YourComponent onChange={onChangeAddress}>
  }
</AddressContainer>

AddressRules

This component contains functionality for easily fetching address formatting rules for a given country. It also smoothly switches between countries as its country prop updates.

The component will then instantiate a Context and provide such rules to any component in its tree. All AddressForm components with a rules prop are automatically injected with the current country rules; it is not necessary to provide them such prop if they are inside an AddressRules component.

Props

AddressRules.propTypes = {
  children: PropTypes.any.isRequired,
  country: PropTypes.string.isRequired,
  fetch: PropTypes.func.isRequired,
}

Example

<AddressRules
  country={'BRA'}
  fetch={country => import('@vtex/address-form/lib/country/' + country)}
>
  {/* AddressSummary will automatically receive Brazilian formatting */}
  <AddressSummary address={address1} />
</AddressRules>

CountrySelector

Renders a select that shows all the countries options.

Props

CountrySelector.propTypes = {
  Input: PropTypes.func,
  address: AddressShapeWithValidation,
  shipsTo: PropTypes.array.isRequired,
  onChangeAddress: PropTypes.func.isRequired,
}

Example

<AddressContainer
  address={address}
  rules={selectedRules}
  onChangeAddress={this.handleAddressChange}
>
  {onChangeAddress => (
    <CountrySelector
      Input={DefaultInput}
      address={address}
      shipsTo={shipsTo}
      onChangeAddress={onChangeAddress}
    />
  )}
</AddressContainer>

AddressForm

Renders an address form base on rules of the selected country.

Props

AddressForm.propTypes = {
  Input: PropTypes.func,
  address: AddressShapeWithValidation,
  omitPostalCodeFields: PropTypes.bool,
  omitAutoCompletedFields: PropTypes.bool,
  rules: PropTypes.object.isRequired,
  onChangeAddress: PropTypes.func.isRequired,
}

Example

<AddressContainer
  address={address}
  rules={selectedRules}
  onChangeAddress={this.handleAddressChange}
>
  {onChangeAddress => (
    <AddressForm
      Input={DefaultInput}
      address={address}
      rules={selectedRules}
      onChangeAddress={onChangeAddress}
    />
  )}
</AddressContainer>

AddressSummary

Renders a summary of the address.

Props

AddressSummary.propTypes = {
  canEditData: PropTypes.bool,
  address: AddressShape.isRequired,
  rules: PropTypes.object.isRequired,
  giftRegistryDescription: PropTypes.string,
  onClickMaskedInfoIcon: PropTypes.func,
}

Example

<AddressSummary
  address={removeValidation(address)}
  rules={selectedRules}
  onClickMaskedInfoIcon={this.handleClickMaskedInfoIcon}
/>

PostalCodeGetter

Renders the requried components to get the postal code of an address. Some countries you can get the postal code by one simple and direct input, but in other countries we must render some select fields so that the user may select a place that we assign a defined postal code.

Props

PostalCodeGetter.propTypes = {
  address: AddressShapeWithValidation,
  Button: PropTypes.func,
  Input: PropTypes.func,
  onChangeAddress: PropTypes.func.isRequired,
  onSubmit: PropTypes.func,
  rules: PropTypes.object.isRequired,
  submitLabel: PropTypes.string,
}

Example

<AddressContainer
  address={address}
  rules={selectedRules}
  onChangeAddress={this.handleAddressChange}
>
  {onChangeAddress => (
    <div>
      <PostalCodeGetter
        address={address}
        Input={DefaultInput}
        Button={DefaultSubmitButton}
        rules={selectedRules}
        onChangeAddress={onChangeAddress}
        onSubmit={this.handleSubmit}
        submitLabel={'Estimate'}
      />
    </div>
  )}
</AddressContainer>

AutoCompletedFields

Renders a summary of the fields that were auto completed by the postal code or by the geolocation.

Props

AutoCompletedFields.propTypes = {
  children: PropTypes.node.isRequired,
  address: AddressShapeWithValidation,
  rules: PropTypes.object.isRequired,
  onChangeAddress: PropTypes.func.isRequired,
}

Example

<AddressContainer
  address={address}
  rules={selectedRules}
  onChangeAddress={this.handleAddressChange}
>
  {onChangeAddress => (
    <div>
      <AutoCompletedFields
        address={address}
        rules={selectedRules}
        onChangeAddress={onChangeAddress}
      >
        <a className="link-edit" id="force-shipping-fields">
          {intl.formatMessage({ id: 'address-form.edit' })}
        </a>
      </AutoCompletedFields>
    </div>
  )}
</AddressContainer>

AddressSubmitter

This component provides adequated space for adding submit-related components (such as a button) to the form. It receives an onSubmit function and will call it to inform the results of validating the form. It provides a render prop with a handleSubmit hook that children might call when they want to submit the form.

Props

AddressSubmitter.propTypes = {
  onSubmit: PropTypes.func.isRequired,
  rules: PropTypes.object.isRequired,
  address: AddressShapeWithValidation,
  onChangeAddress: PropTypes.func.isRequired,
  children: PropTypes.func.isRequired,
}

Example

<AddressSubmitter onSubmit={this.handleSubmit}>
  {handleSubmit => (
    <Button size="small" block onClick={handleSubmit}>
      Submit
    </Button>
  )}
</AddressSubmitter>

Geolocation Components

Important:

GoogleMapsContainer

This component handles the loading of the Google Maps JavaScript Library.

It provides an object with { loading, googleMaps } to the child function.

Props

GoogleMapsContainer.propTypes = {
  children: PropTypes.func.isRequired,
  apiKey: PropTypes.string.isRequired,
  locale: PropTypes.string.isRequired,
}

Example

<GoogleMapsContainer apiKey={googleMapsAPIKey} locale={locale}>
  {({ loading, googleMaps }) => {
    ...
  }
</GoogleMapsContainer>

GeolocationInput

Renders an input with the Google Maps auto complete feature. When the user selects an option suggested, it fills the address fields using the specified rules defined by the country and its geocoordinates.

Props

GeolocationInput.propTypes = {
  Input: PropTypes.func,
  inputProps: PropTypes.object,
  rules: PropTypes.object.isRequired,
  address: AddressShapeWithValidation.isRequired,
  onChangeAddress: PropTypes.func.isRequired,
  loadingGoogle: PropTypes.bool,
  googleMaps: PropTypes.object,
}

Example

<GoogleMapsContainer apiKey={googleMapsAPIKey} locale={locale}>
  {({ loading, googleMaps }) => (
    <div>
      <GeolocationInput
        loadingGoogle={loading}
        googleMaps={googleMaps}
        address={address}
        rules={selectedRules}
        onChangeAddress={onChangeAddress}
      />
    </div>
  )}
</GoogleMapsContainer>

Map

Renders a Google Map with a marker at given coordinates. Will use coordinates from the geoCoordinates prop or pluck them from the address provided at the address prop if the former is not available.

Props

Map.propTypes = {
  loadingElement: PropTypes.node,
  mapProps: PropTypes.object,
  geoCoordinates: PropTypes.array,
  address: AddressShapeWithValidation,
  rules: PropTypes.object.isRequired,
  onChangeAddress: PropTypes.func.isRequired,
  loadingGoogle: PropTypes.bool,
  googleMaps: PropTypes.object,
}

Example

<GoogleMapsContainer apiKey={googleMapsAPIKey} locale={locale}>
  {({ loading, googleMaps }) => (
    <div>
      {address.geoCoordinates &&
        address.geoCoordinates.valid &&
        address.geoCoordinates.value.length === 2 && (
          <Map
            loadingGoogle={loading}
            googleMaps={googleMaps}
            geoCoordinates={address.geoCoordinates.value}
            rules={selectedRules}
            onChangeAddress={onChangeAddress}
            mapProps={{
              style: {
                height: '120px',
                marginBottom: '10px',
                width: '260px',
              },
            }}
          />
        )}
    </div>
  )}
</GoogleMapsContainer>

Helper Functions

addValidation

Params

Returns

Example

const address = {
  addressId: '10',
  addressType: 'residential',
  city: null,
  complement: null,
  country: 'BRA',
  geoCoordinates: [],
  neighborhood: null,
  number: null,
  postalCode: null,
  receiverName: null,
  reference: null,
  state: null,
  street: null,
  addressQuery: null,
}

addValidation(address)
// {
//   addressId: { value: '10' },
//   addressType: { value: 'residential' },
//   city: { value: null },
//   complement: { value: null },
//   country: { value: 'BRA' },
//   geoCoordinates: { value: [] },
//   neighborhood: { value: null },
//   number: { value: null },
//   postalCode: { value: null },
//   receiverName: { value: null },
//   reference: { value: null },
//   state: { value: null },
//   street: { value: null },
//   addressQuery: { value: null },
// }

removeValidation

Params

Returns

Example

const address = {
  addressId: { value: '10' },
  addressType: { value: 'residential' },
  city: { value: null },
  complement: { value: null },
  country: { value: 'BRA' },
  geoCoordinates: { value: [] },
  neighborhood: { value: null },
  number: { value: null },
  postalCode: { value: null },
  receiverName: { value: null },
  reference: { value: null },
  state: { value: null },
  street: { value: null },
  addressQuery: { value: null },
}

removeValidation(address)
// {
//   addressId: '10',
//   addressType: 'residential',
//   city: null,
//   complement: null,
//   country: 'BRA',
//   geoCoordinates: [],
//   neighborhood: null,
//   number: null,
//   postalCode: null,
//   receiverName: null,
//   reference: null,
//   state: null,
//   street: null,
//   addressQuery: null,
// }

isValidAddress

Params

Returns

Example

const address = {
  addressId: { value: '10' },
  addressType: { value: 'residential' },
  city: { value: 'Rio de Janeiro' },
  complement: { value: null },
  country: { value: 'BRA' },
  geoCoordinates: { value: [] },
  neighborhood: { value: 'Botafogo' },
  number: { value: '300' },
  postalCode: { value: '22231000' },
  receiverName: { value: 'João' },
  reference: { value: null },
  state: { value: 'RJ' },
  street: { value: '' },
  addressQuery: { value: null },
}

isValidAddress(address, rules)
// {
//   valid: false,
//   address: {
//     addressId: { value: '10' },
//     addressType: { value: 'residential' },
//     city: { value: 'Rio de Janeiro' },
//     complement: { value: null },
//     country: { value: 'BRA' },
//     geoCoordinates: { value: [] },
//     neighborhood: { value: 'Botafogo' },
//     number: { value: '300' },
//     postalCode: { value: '22231000' },
//     receiverName: { value: 'João' },
//     reference: { value: null },
//     state: { value: 'RJ' },
//     street: { value: '', valid: false, focus: true, reason: 'ERROR_EMPTY_FIELD' },
//     addressQuery: { value: null },
//   }
// }

validateField

Params

validateField(value, name, address, rules)

Returns

An object with the properties:

Example

const address = {
  addressId: { value: '10' },
  addressType: { value: 'residential' },
  city: { value: '' },
  complement: { value: null },
  country: { value: 'BRA' },
  geoCoordinates: { value: [] },
  neighborhood: { value: '' },
  number: { value: '' },
  postalCode: { value: '222' },
  receiverName: { value: 'João' },
  reference: { value: null },
  state: { value: '' },
  street: { value: '' },
  addressQuery: { value: null },
}

validateField('222', 'postalCode', address, rules)
// {
//    valid: false,
//    reason: 'ERROR_POSTAL_CODE'
// }

Public modules

locales/

This folder provides JSONs of translations commonly used by the Address Form in a flat format.

country/

This folder provides the country rules modules.

Each country has their own set of rules on how an address form should be rendered. They specify:

If a selected country does not exists in the country folder. Use the default.js

inputs/

This folder provides inputs to be used as building blocks for the address form.

Types

AddressShape

AddressShapeWithValidation