Skip to content

Commit

Permalink
Merge pull request #354 from reactioncommerce/345-machikoyasuda-regio…
Browse files Browse the repository at this point in the history
…ninput-component

feat(regioninput): add RegionInput component, use in AddressForm
  • Loading branch information
aldeed committed Nov 2, 2018
2 parents 5c3ee7f + a9806dd commit 51a8816
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 26 deletions.
45 changes: 19 additions & 26 deletions package/src/components/AddressForm/v1/AddressForm.js
Expand Up @@ -74,7 +74,12 @@ class AddressForm extends Component {
* Pass either the Reaction PhoneNumberInput component or your own component that is
* compatible with ReactoForm.
*/
PhoneNumberInput: CustomPropTypes.component.isRequired
PhoneNumberInput: CustomPropTypes.component.isRequired,
/**
* Pass either the Reaction RegionInput component or your own component that is
* compatible with ReactoForm.
*/
RegionInput: CustomPropTypes.component.isRequired
}).isRequired,
/**
* Errors array
Expand Down Expand Up @@ -107,7 +112,7 @@ class AddressForm extends Component {
continent: PropTypes.string,
capital: PropTypes.string,
currency: PropTypes.string,
languges: PropTypes.string,
languages: PropTypes.string,
states: PropTypes.objectOf(PropTypes.shape({ name: PropTypes.string }))
})),
/**
Expand Down Expand Up @@ -151,9 +156,9 @@ class AddressForm extends Component {
isOnDarkBackground: false,
isSaving: false,
name: "address",
onCancel() {},
onChange() {},
onSubmit() {},
onCancel() { },
onChange() { },
onSubmit() { },
shouldShowAddressNameField: false,
shouldShowIsCommercialField: false,
validator: getRequiredValidator(
Expand Down Expand Up @@ -258,7 +263,7 @@ class AddressForm extends Component {
addressNamePlaceholder,
value,
className,
components: { Checkbox, ErrorsBlock, Field, TextInput, Select, PhoneNumberInput },
components: { Checkbox, ErrorsBlock, Field, TextInput, Select, PhoneNumberInput, RegionInput },
errors,
isOnDarkBackground,
isSaving,
Expand Down Expand Up @@ -389,26 +394,14 @@ class AddressForm extends Component {

<ColHalf>
<Field name="region" label="Region" labelFor={regionInputId} isRequired>
{this.regionOptions && this.regionOptions.length > 1 ? (
<Select
id={regionInputId}
alphabetize
isSearchable
name="region"
options={this.regionOptions}
placeholder="Region"
isOnDarkBackground={isOnDarkBackground}
isReadOnly={isSaving}
/>
) : (
<TextInput
id={regionInputId}
name="region"
placeholder="Region"
isOnDarkBackground={isOnDarkBackground}
isReadOnly={isSaving}
/>
)}
<RegionInput
id={regionInputId}
options={this.regionOptions}
isOnDarkBackground={isOnDarkBackground}
isReadOnly={isSaving}
name="region"
placeholder="Region"
/>
<ErrorsBlock names={["region"]} />
</Field>
</ColHalf>
Expand Down
81 changes: 81 additions & 0 deletions package/src/components/RegionInput/v1/RegionInput.js
@@ -0,0 +1,81 @@
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import { withComponents } from "@reactioncommerce/components-context";
import { CustomPropTypes } from "../../../utils";

class RegionInput extends Component {
static propTypes = {
/**
* 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 TextInput component or your own component that is
* compatible with ReactoForm.
*/
TextInput: CustomPropTypes.component.isRequired,
/**
* Pass either the Reaction Select component or your own component that is
* compatible with ReactoForm.
*/
Select: CustomPropTypes.component.isRequired
}).isRequired,
/**
* Name of input
*/
name: PropTypes.string.isRequired,
/**
* Region options to populate the form's region fields
*/
options: PropTypes.array,
/**
* Prepopulate the input's value
*/
value: PropTypes.string
};

render() {
const {
className,
components: { Select, TextInput },
options,
value,
...props
} = this.props;

return (
<Fragment>
{
options && options.length > 1 ? (
<Select
className={className}
alphabetize
isSearchable
options={options}
value={value}
{...props}
/>
) : (
<TextInput
className={className}
value={value}
{...props}
/>
)
}
</Fragment>
);
}
}

export default withComponents(RegionInput);
86 changes: 86 additions & 0 deletions package/src/components/RegionInput/v1/RegionInput.md
@@ -0,0 +1,86 @@
### Overview

The `RegionInput` component renders either a `Select` or `TextInput`, based on the number of region options.

### Usage

Use the `RegionInput` in a form with country and region locales. In the `AddressForm` component, for example, the form's `activeCountry` state, set by the country select, will trigger the `RegionInput` to appear as a plain text input or a dropdown of regions specified in a locale object or file.

Pass any related form input props, like `isReadOnly`, `placeholder`, `maxLength`, `name`, `id` and more through from the parent component into `RegionInput`. See [Select](./#!/Select) and [TextInput](./#!/TextInput) for more props.

#### With a value in a TextInput

```jsx
<RegionInput name="region" isOnDarkBackground isReadOnly value="California" />
```

#### With a value in a Select

```jsx
const options = [
{
"value": "AA",
"label": "A State"
},
{
"value": "BB",
"label": "B State"
}
];

<RegionInput name="region" options={options} value="BB" />
```

#### Implemented in an AddressForm

```jsx
const locales = {
"NR": {
"name": "Country Without Regions",
},
"WR": {
"name": "Country With Regions",
"states": {
"AA": {
"name": "A State"
},
"BB": {
"name": "B State"
}
}
}
};

class AddressExample extends React.Component {
constructor(props) {
super(props);
this._addressForm;
this.bindForm.bind(this);
}

bindForm(formEl) {
if (formEl) {
this._addressForm = formEl;
}
}

render() {
return (
<div>
<AddressForm
locales={locales}
ref={(formEl) => { this.bindForm(formEl) }}
onSubmit={(address) => console.log("Address submitted", address)}
/>
<Button onClick={() => this._addressForm.submit()}>Submit</Button>
</div>
);
}
}

<AddressExample />
```

### Theme

Refer to [Select](./#!/Select), [TextInput](./#!/TextInput) and [AddressForm](./#!/AddressForm) themes.
61 changes: 61 additions & 0 deletions package/src/components/RegionInput/v1/RegionInput.test.js
@@ -0,0 +1,61 @@
import React from "react";
import renderer from "react-test-renderer";
import mockComponents from "../../../tests/mockComponents";
import RegionInput from "./RegionInput";

test("basic snapshot with only the components and required props should render a TextInput", () => {
const component = renderer.create(<RegionInput name="region" components={mockComponents} />);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

test("basic snapshot with other form props", () => {
const component = renderer.create(<RegionInput components={mockComponents} value="California" name="region" isReadOnly />);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

test("basic snapshot with pre-filled value in TextInput", () => {
const component = renderer.create(<RegionInput name="region" components={mockComponents} value="California"/>);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

test("basic snapshot with pre-filled value in Select", () => {
const options = [
{
value: "AA",
label: "A State"
},
{
value: "BB",
label: "B State"
}
];

const component = renderer.create(<RegionInput name="region" components={mockComponents} options={options} value="BB"/>);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

test("basic snapshot with options should render a Select", () => {
const options = [
{
value: "AA",
label: "A State"
},
{
value: "BB",
label: "B State"
}
];

const component = renderer.create(<RegionInput name="region" components={mockComponents} options={options}/>);

const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`basic snapshot with only the components and required props should render a TextInput 1`] = `"TextInput(undefined)"`;

exports[`basic snapshot with options should render a Select 1`] = `"Select(undefined)"`;

exports[`basic snapshot with other form props 1`] = `"TextInput(undefined)"`;

exports[`basic snapshot with pre-filled value in Select 1`] = `"Select(undefined)"`;

exports[`basic snapshot with pre-filled value in TextInput 1`] = `"TextInput(undefined)"`;
1 change: 1 addition & 0 deletions package/src/components/RegionInput/v1/index.js
@@ -0,0 +1 @@
export { default } from "./RegionInput";
1 change: 1 addition & 0 deletions package/src/tests/mockComponents.js
Expand Up @@ -62,6 +62,7 @@ function stringifyJSONCircularSafe(obj) {
"ProgressiveImage",
"ProfileImage",
"QuantityInput",
"RegionInput",
"Select",
"StockWarning",
"TextInput",
Expand Down
1 change: 1 addition & 0 deletions styleguide.config.js
Expand Up @@ -423,6 +423,7 @@ module.exports = {
"Field",
"MultiSelect",
"PhoneNumberInput",
"RegionInput",
"QuantityInput",
"Select",
"SelectableItem",
Expand Down
2 changes: 2 additions & 0 deletions styleguide/src/appComponents.js
Expand Up @@ -40,6 +40,7 @@ import Price from "../../package/src/components/Price/v1";
import ProfileImage from "../../package/src/components/ProfileImage/v1";
import ProgressiveImage from "../../package/src/components/ProgressiveImage/v1";
import QuantityInput from "../../package/src/components/QuantityInput/v1";
import RegionInput from "../../package/src/components/RegionInput/v1";
import Select from "../../package/src/components/Select/v1";
import StockWarning from "../../package/src/components/StockWarning/v1";
import StripeForm from "../../package/src/components/StripeForm/v1";
Expand Down Expand Up @@ -95,6 +96,7 @@ export default {
ProfileImage,
ProgressiveImage,
QuantityInput,
RegionInput,
Select,
spinner,
StockWarning,
Expand Down
4 changes: 4 additions & 0 deletions styleguide/src/sections/InstallingandImporting.md
Expand Up @@ -58,6 +58,7 @@ import CheckoutActionComplete from "@reactioncommerce/components/CheckoutActionC
import CheckoutActionIncomplete from "@reactioncommerce/components/CheckoutActionIncomplete/v1";
import ErrorsBlock from "@reactioncommerce/components/ErrorsBlock/v1";
import Field from "@reactioncommerce/components/Field/v1";
import InlineAlert from "@reactioncommerce/components/InlineAlert/v1";
import InPageMenuItem from "@reactioncommerce/components/InPageMenuItem/v1";
import Link from "@reactioncommerce/components/Link/v1";
import MiniCartSummary from "@reactioncommerce/components/MiniCartSummary/v1";
Expand All @@ -67,6 +68,7 @@ import Price from "@reactioncommerce/components/Price/v1";
import ProfileImage from "@reactioncommerce/components/ProfileImage/v1";
import ProgressiveImage from "@reactioncommerce/components/ProgressiveImage/v1";
import QuantityInput from "@reactioncommerce/components/QuantityInput/v1";
import RegionInput from "@reactioncommerce/components/RegionInput/v1";
import Select from "@reactioncommerce/components/Select/v1";
import StockWarning from "@reactioncommerce/components/StockWarning/v1";
import StripeForm from "@reactioncommerce/components/StripeForm/v1";
Expand Down Expand Up @@ -106,6 +108,7 @@ export default {
iconLock,
iconMastercard,
iconVisa,
InlineAlert,
InPageMenuItem,
Link,
MiniCartSummary,
Expand All @@ -115,6 +118,7 @@ export default {
ProfileImage,
ProgressiveImage,
QuantityInput,
RegionInput,
Select,
spinner,
StockWarning,
Expand Down

0 comments on commit 51a8816

Please sign in to comment.