Skip to content

Commit

Permalink
Merge pull request #388 from reactioncommerce/feat-aldeed-payment-che…
Browse files Browse the repository at this point in the history
…ckout-components

New checkout components for multiple payments
  • Loading branch information
kieckhafer authored Jan 11, 2019
2 parents 0acb38e + ec655f1 commit 68e8402
Show file tree
Hide file tree
Showing 39 changed files with 2,667 additions and 162 deletions.
1 change: 1 addition & 0 deletions package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
},
"dependencies": {
"@material-ui/core": "^3.1.0",
"accounting-js": "^1.1.1",
"lodash.debounce": "^4.0.8",
"lodash.get": "^4.4.2",
"lodash.isempty": "^4.4.0",
Expand Down
42 changes: 19 additions & 23 deletions package/src/components/AddressCapture/v1/AddressCapture.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ class AddressCapture extends Component {
*/
errors: PropTypes.arrayOf(PropTypes.shape({
/**
* Error message
*/
* Error message
*/
message: PropTypes.string.isRequired,
/**
* Error name
*/
* Error name
*/
name: PropTypes.string.isRequired
})),
/**
Expand Down Expand Up @@ -134,57 +134,51 @@ class AddressCapture extends Component {
_form = null;

/**
* @method hasAddressSuggestion
* @summary returns true if we have a suggested address from a address validation service
* @return {Boolean} - true if address suggestion on props
* @return {Boolean} True if address suggestion on props
*/
get hasAddressSuggestion() {
const { addressReviewProps: { addressSuggestion } } = this.props;
return !!addressSuggestion;
}

/**
* @method hasValidationError
* @summary returns true if we have any validation errors from a address validation service
* @return {Boolean} - true if validation errors on props
* @return {Boolean} True if validation errors on props
*/
get hasValidationError() {
const { addressReviewProps: { validationError } } = this.props;
return !!validationError;
}

/**
* @method addressEntered
* @summary getter that returns the entered address
* @return {Object} addressEntered - Address object
* @return {Object} Address object
*/
get addressEntered() {
const { addressReviewProps: { addressEntered } } = this.props;
return addressEntered && addressEntered;
}

/**
* @method addressSuggestion
* @summary getter that returns the suggested address
* @return {Object} addressSuggestion - Address object
* @return {Object} Address object
*/
get addressSuggestion() {
const { addressReviewProps: { addressSuggestion } } = this.props;
return addressSuggestion && addressSuggestion;
}

/**
* @method addressProvided
* @summary getter that returns the provided address form value
* @return {Object} addressProvided - Address object
* @return {Object} Address object
*/
get addressProvided() {
const { addressFormProps: { value } } = this.props;
return value && value;
}

/**
* @method inEntry
* @summary getter that returns true if in entry mode
* @return {Boolean} True if currently in entry status
*/
Expand All @@ -194,7 +188,6 @@ class AddressCapture extends Component {
}

/**
* @method inEdit
* @summary getter that returns true if in edit mode
* @return {Boolean} True if currently in edit status
*/
Expand All @@ -204,7 +197,6 @@ class AddressCapture extends Component {
}

/**
* @method inReview
* @summary getter that returns true if in review mode
* @return {Boolean} True if currently in review status
*/
Expand All @@ -214,40 +206,44 @@ class AddressCapture extends Component {
}

/**
* @method toggleStatus
* @summary setter that toggles the Component's status.
* @param {String} status The new status
* @return {undefined}
*/
set toggleStatus(status) {
this.setState({ status });
}

/* eslint-disable valid-jsdoc */
/**
* @method formRef
* @summary binds the active form element to the `_form` property
* @param {Object} form - React ref element
* @return {undefined}
* @param {Object} form React ref element
* Can't include "return {undefined}" because react-docgen doesn't support it.
*/
/* eslint-enable valid-jsdoc */
formRef = (form) => {
this._form = form;
};

/* eslint-disable valid-jsdoc */
/**
* @method submit
* @summary Instance method that submits the form, this allows a parent component access to the Form submit event.
* @return {undefined}
* Can't include "return {undefined}" because react-docgen doesn't support it.
*/
/* eslint-enable valid-jsdoc */
submit = () => {
this._form.submit();
};

/* eslint-disable valid-jsdoc */
/**
* @method handleSubmit
* @summary validate or submit the entered address object.
* @param {Object} address - submitted address object
* @return {undefined}
* Can't include "return {undefined}" because react-docgen doesn't support it.
*/
/* eslint-enable valid-jsdoc */
handleSubmit = async (address) => {
const { onAddressValidation, onSubmit } = this.props;
if (onAddressValidation && !address.isValid) {
Expand Down
133 changes: 133 additions & 0 deletions package/src/components/AddressChoice/v1/AddressChoice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { withComponents } from "@reactioncommerce/components-context";
import { addressToString, CustomPropTypes } from "../../../utils";

class AddressChoice extends Component {
static propTypes = {
/**
* A list of addresses to show for selection
*/
addresses: CustomPropTypes.addressBook,
/**
* You can provide a `className` prop that will be applied to the outermost DOM element
* rendered by this component. We do not recommend using this for styling purposes, but
* it can be useful as a selector in some situations.
*/
className: PropTypes.string,
/**
* If you've set up a components context using
* [@reactioncommerce/components-context](https://github.com/reactioncommerce/components-context)
* (recommended), then this prop will come from there automatically. If you have not
* set up a components context or you want to override one of the components in a
* single spot, you can pass in the components prop directly.
*/
components: PropTypes.shape({
/**
* Pass either the Reaction AddressForm component or your own component that
* accepts compatible props.
*/
AddressForm: CustomPropTypes.component.isRequired,
/**
* A reaction SelectableList component or compatible component.
*/
SelectableList: CustomPropTypes.component.isRequired
}),
/**
* If true, choosing an address and typing in address fields is disabled
*/
isReadOnly: PropTypes.bool,
/**
* Called with an address whenever the selected or entered
* address changes. If they selected one, it will be the
* complete address that was passed in `addresses`. If they're
* entering one, it will be whatever they have entered so far
* and may be partial.
*/
onChange: PropTypes.func,
/**
* The label for the "Use a different address" selection item, if it
* is shown.
*/
otherAddressLabel: PropTypes.string
};

static defaultProps = {
isReadOnly: false,
onChange() {},
otherAddressLabel: "Use a different address"
};

constructor(props) {
super(props);

let selectedOption = "OTHER";
if (Array.isArray(props.addresses) && props.addresses.length > 0) {
selectedOption = "0";
}

this.state = { selectedOption };
}

handleChangeAddress = (address) => {
this.props.onChange(address);
}

handleChangeSelection = (selectedOption) => {
const { addresses } = this.props;

this.setState({ selectedOption });

if (selectedOption !== "OTHER" && Array.isArray(addresses)) {
this.props.onChange(addresses[Number(selectedOption)]);
}
}

renderSelectList() {
const {
addresses,
components: { SelectableList },
isReadOnly,
otherAddressLabel
} = this.props;
const { selectedOption } = this.state;

if (!Array.isArray(addresses) || addresses.length === 0) return null;

const listOptions = addresses.map((address, index) => ({
id: String(index),
label: addressToString(address, { includeFullName: true }),
value: String(index)
}));

listOptions.push({
id: "OTHER",
label: otherAddressLabel,
value: "OTHER"
});

return (
<SelectableList
name="addressList"
isReadOnly={isReadOnly}
onChange={this.handleChangeSelection}
options={listOptions}
value={selectedOption}
/>
);
}

render() {
const { className, components: { AddressForm }, isReadOnly } = this.props;
const { selectedOption } = this.state;

return (
<div className={className}>
{this.renderSelectList()}
{selectedOption === "OTHER" && <AddressForm isReadOnly={isReadOnly} onChange={this.handleChangeAddress} />}
</div>
);
}
}

export default withComponents(AddressChoice);
84 changes: 84 additions & 0 deletions package/src/components/AddressChoice/v1/AddressChoice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
### Overview

The `AddressChoice` component is a way of collecting an address while giving an option of choosing a previously saved address. If no previously saved addresses are provided, it will render an [AddressForm](./#!/AddressForm). Otherwise it will render a choice list where each address is in the list and the final option is to enter a new address.

### Usage

#### Simple
If you don't pass in any `addresses`, it renders an [AddressForm](./#!/AddressForm).

```jsx
<AddressChoice onChange={console.log.bind(console)} />
```

#### Read Only
Use `isReadOnly` prop to disable editing, such as when submitting the form or loading something.

```jsx
<AddressChoice isReadOnly />
```

#### With Addresses
When you provide one or more `addresses`, they are presented in a [SelectableList](./#!/SelectableList) along with a final option that shows the [AddressForm](./#!/AddressForm) when selected.

`onChange` prop is called whenever the address changes, whether by selecting an existing address or changing a field in the custom address form.

```jsx
const addresses = [
{
_id: "20",
address1: "7742 Hwy 23",
address2: "",
country: "US",
city: "Belle Chasse",
fullName: "Salvos Seafood",
postal: "70037",
region: "LA",
phone: "(504) 393-7303"
},
{
_id: "21",
address1: "35 Akin Adesola St",
address2: "",
country: "NG",
city: "Lagos",
fullName: "Ocean Basket Victoria Island",
postal: "101241",
region: "Victoria Island",
phone: "234 816 059 1821"
}
];

<AddressChoice addresses={addresses} onChange={console.log.bind(console)} />
```

#### Read Only With Addresses

```jsx
const addresses = [
{
_id: "20",
address1: "7742 Hwy 23",
address2: "",
country: "US",
city: "Belle Chasse",
fullName: "Salvos Seafood",
postal: "70037",
region: "LA",
phone: "(504) 393-7303"
},
{
_id: "21",
address1: "35 Akin Adesola St",
address2: "",
country: "NG",
city: "Lagos",
fullName: "Ocean Basket Victoria Island",
postal: "101241",
region: "Victoria Island",
phone: "234 816 059 1821"
}
];

<AddressChoice addresses={addresses} isReadOnly onChange={console.log.bind(console)} />
```
Loading

0 comments on commit 68e8402

Please sign in to comment.