Add international phone validation

This commit is contained in:
Ray Schamp 2016-06-02 15:25:02 -04:00
parent bcda082014
commit fe74208f32
15 changed files with 127 additions and 49 deletions

View file

@ -47,6 +47,7 @@
"formsy-react-components": "0.7.1", "formsy-react-components": "0.7.1",
"git-bundle-sha": "0.0.2", "git-bundle-sha": "0.0.2",
"glob": "5.0.15", "glob": "5.0.15",
"google-libphonenumber": "1.0.21",
"json-loader": "0.5.2", "json-loader": "0.5.2",
"json2po-stream": "1.0.3", "json2po-stream": "1.0.3",
"jsx-loader": "0.13.2", "jsx-loader": "0.13.2",
@ -69,6 +70,7 @@
"react-onclickoutside": "4.1.1", "react-onclickoutside": "4.1.1",
"react-redux": "4.4.5", "react-redux": "4.4.5",
"react-slick": "0.12.2", "react-slick": "0.12.2",
"react-telephone-input": "3.4.5",
"redux": "3.5.2", "redux": "3.5.2",
"redux-thunk": "2.0.1", "redux-thunk": "2.0.1",
"sass-lint": "1.5.1", "sass-lint": "1.5.1",

View file

@ -1,7 +1,7 @@
var classNames = require('classnames'); var classNames = require('classnames');
var FRCCheckboxGroup = require('formsy-react-components').CheckboxGroup; var FRCCheckboxGroup = require('formsy-react-components').CheckboxGroup;
var React = require('react'); var React = require('react');
var validateMixin = require('./validateMixin.jsx'); var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
var CheckboxGroup = React.createClass({ var CheckboxGroup = React.createClass({
type: 'CheckboxGroup', type: 'CheckboxGroup',
@ -16,4 +16,4 @@ var CheckboxGroup = React.createClass({
} }
}); });
module.exports = validateMixin(CheckboxGroup); module.exports = defaultValidationHOC(CheckboxGroup);

View file

@ -1,7 +1,7 @@
var classNames = require('classnames'); var classNames = require('classnames');
var FRCCheckbox = require('formsy-react-components').Checkbox; var FRCCheckbox = require('formsy-react-components').Checkbox;
var React = require('react'); var React = require('react');
var validateMixin = require('./validateMixin.jsx'); var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
var Checkbox = React.createClass({ var Checkbox = React.createClass({
type: 'Checkbox', type: 'Checkbox',
@ -16,4 +16,4 @@ var Checkbox = React.createClass({
} }
}); });
module.exports = validateMixin(Checkbox); module.exports = defaultValidationHOC(Checkbox);

View file

@ -1,7 +1,7 @@
var classNames = require('classnames'); var classNames = require('classnames');
var Formsy = require('formsy-react'); var Formsy = require('formsy-react');
var React = require('react'); var React = require('react');
var validations = require('./validations'); var validations = require('./validations.jsx').validations;
for (var validation in validations) { for (var validation in validations) {
Formsy.addValidationRule(validation, validations[validation]); Formsy.addValidationRule(validation, validations[validation]);

View file

@ -1,7 +1,7 @@
var classNames = require('classnames'); var classNames = require('classnames');
var FRCInput = require('formsy-react-components').Input; var FRCInput = require('formsy-react-components').Input;
var React = require('react'); var React = require('react');
var validateMixin = require('./validateMixin.jsx'); var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
require('./input.scss'); require('./input.scss');
@ -22,4 +22,4 @@ var Input = React.createClass({
} }
}); });
module.exports = validateMixin(Input); module.exports = defaultValidationHOC(Input);

View file

@ -0,0 +1,55 @@
var classNames = require('classnames');
var React = require('react');
var FormsyMixin = require('formsy-react').Mixin;
var ReactPhoneInput = require('react-telephone-input/lib/withStyles');
var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
var validationHOCFactory = require('./validations.jsx').validationHOCFactory;
var Row = require('formsy-react-components').Row;
var ComponentMixin = require('formsy-react-components').ComponentMixin;
var PhoneInput = React.createClass({
displayName: 'PhoneInput',
mixins: [
FormsyMixin,
ComponentMixin
],
getDefaultProps: function () {
return {
validations: {
isPhone: true
},
flagsImagePath: '/images/flags.png'
};
},
onChangeInput: function (number, country) {
var value = {national_number: number, country_code: country};
this.setValue(value);
this.props.onChange(this.props.name, value);
},
render: function () {
return (
<Row {... this.getRowProperties()}
htmlFor={this.getId()}
className={classNames('phone-input', this.props.className)}
>
<div className="input-group">
<ReactPhoneInput className="form-control"
{... this.props}
onChange={this.onChangeInput}
id={this.getId()}
label={null}
disabled={this.isFormDisabled() || this.props.disabled}
/>
</div>
{this.renderHelp()}
{this.renderErrorMessage()}
</Row>
);
}
});
var phoneValidationHOC = validationHOCFactory({
isPhone: 'Please enter a valid phone number'
});
module.exports = defaultValidationHOC(phoneValidationHOC(PhoneInput));

View file

@ -1,7 +1,7 @@
var classNames = require('classnames'); var classNames = require('classnames');
var FRCRadioGroup = require('formsy-react-components').RadioGroup; var FRCRadioGroup = require('formsy-react-components').RadioGroup;
var React = require('react'); var React = require('react');
var validateMixin = require('./validateMixin.jsx'); var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
var RadioGroup = React.createClass({ var RadioGroup = React.createClass({
type: 'RadioGroup', type: 'RadioGroup',
@ -16,4 +16,4 @@ var RadioGroup = React.createClass({
} }
}); });
module.exports = validateMixin(RadioGroup); module.exports = defaultValidationHOC(RadioGroup);

View file

@ -2,7 +2,7 @@ var classNames = require('classnames');
var defaults = require('lodash.defaultsdeep'); var defaults = require('lodash.defaultsdeep');
var FRCSelect = require('formsy-react-components').Select; var FRCSelect = require('formsy-react-components').Select;
var React = require('react'); var React = require('react');
var validateMixin = require('./validateMixin.jsx'); var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
require('./select.scss'); require('./select.scss');
@ -26,4 +26,4 @@ var Select = React.createClass({
} }
}); });
module.exports = validateMixin(Select); module.exports = defaultValidationHOC(Select);

View file

@ -1,7 +1,7 @@
var classNames = require('classnames'); var classNames = require('classnames');
var FRCTextarea = require('formsy-react-components').Textarea; var FRCTextarea = require('formsy-react-components').Textarea;
var React = require('react'); var React = require('react');
var validateMixin = require('./validateMixin.jsx'); var defaultValidationHOC = require('./validations.jsx').defaultValidationHOC;
require('./textarea.scss'); require('./textarea.scss');
@ -18,4 +18,4 @@ var TextArea = React.createClass({
} }
}); });
module.exports = validateMixin(TextArea); module.exports = defaultValidationHOC(TextArea);

View file

@ -1,24 +0,0 @@
var defaults = require('lodash.defaultsdeep');
var React = require('react');
var validateMixin = function (Component) {
var ValidatedComponent = React.createClass({
getDefaultValidationErrors: function () {
return {
isDefaultRequiredValue: 'This field is required'
};
},
render: function () {
var validationErrors = defaults(
this.getDefaultValidationErrors(),
this.props.validationErrors
);
return (
<Component {...this.props} validationErrors={validationErrors} />
);
}
});
return ValidatedComponent;
};
module.exports = validateMixin;

View file

@ -1,10 +0,0 @@
var Validations = {
notEquals: function (values, value, neq) {
return value !== neq;
},
notEqualsField: function (values, value, field) {
return value !== values[field];
}
};
module.exports = Validations;

View file

@ -0,0 +1,46 @@
var defaults = require('lodash.defaultsdeep');
var libphonenumber = require('google-libphonenumber');
var phoneNumberUtil = libphonenumber.PhoneNumberUtil.getInstance();
var React = require('react');
module.exports = {};
module.exports.validations = {
notEquals: function (values, value, neq) {
return value !== neq;
},
notEqualsField: function (values, value, field) {
return value !== values[field];
},
isPhone: function (values, value) {
if (typeof value === 'undefined') return true;
if (value && value.national_number === '+') return true;
try {
var parsed = phoneNumberUtil.parse(value.national_number, value.country_code.iso2);
} catch (err) {
return false;
}
return phoneNumberUtil.isValidNumber(parsed);
}
};
module.exports.validationHOCFactory = function (defaultValidationErrors) {
return function (Component) {
var ValidatedComponent = React.createClass({
render: function () {
var validationErrors = defaults(
defaultValidationErrors,
this.props.validationErrors
);
return (
<Component {...this.props} validationErrors={validationErrors} />
);
}
});
return ValidatedComponent;
};
};
module.exports.defaultValidationHOC = module.exports.validationHOCFactory({
isDefaultRequiredValue: 'This field is required'
});

View file

@ -13,6 +13,7 @@ var FormSet = formset.FormSet;
var FormStep = formset.FormStep; var FormStep = formset.FormStep;
var Input = require('../../components/forms/input.jsx'); var Input = require('../../components/forms/input.jsx');
var Page = require('../../components/page/www/page.jsx'); var Page = require('../../components/page/www/page.jsx');
var PhoneInput = require('../../components/forms/phone-input.jsx');
var RadioGroup = require('../../components/forms/radio-group.jsx'); var RadioGroup = require('../../components/forms/radio-group.jsx');
var Select = require('../../components/forms/select.jsx'); var Select = require('../../components/forms/select.jsx');
var TextArea = require('../../components/forms/textarea.jsx'); var TextArea = require('../../components/forms/textarea.jsx');
@ -152,7 +153,10 @@ var PhoneNumberStep = React.createClass({
Why do we ask for this information <a onClick={this.handle}>?</a> Why do we ask for this information <a onClick={this.handle}>?</a>
</p>}> </p>}>
<Form onValidSubmit={this.props.onNextStep}> <Form onValidSubmit={this.props.onNextStep}>
<Input label="Phone Number" type="tel" name="phone" required /> <PhoneInput label="Phone Number"
name="phone"
defaultCountry="us"
required />
<Checkbox label={ <Checkbox label={
'Yes, I consent to lorem ipsum dolor sit amet, consectetur' + 'Yes, I consent to lorem ipsum dolor sit amet, consectetur' +
'adipiscing elit.' 'adipiscing elit.'

BIN
static/images/flags.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View file

@ -79,11 +79,16 @@ module.exports = {
test: /\.scss$/, test: /\.scss$/,
loader: 'style!css!postcss-loader!sass' loader: 'style!css!postcss-loader!sass'
}, },
{
test: /\.css$/,
loader: 'style!css!postcss-loader'
},
{ {
test: /\.(png|jpg|gif|eot|svg|ttf|woff)$/, test: /\.(png|jpg|gif|eot|svg|ttf|woff)$/,
loader: 'url-loader' loader: 'url-loader'
} }
] ],
noParse: /node_modules\/google-libphonenumber\/dist/
}, },
postcss: function () { postcss: function () {
return [autoprefixer({browsers: ['last 3 versions']})]; return [autoprefixer({browsers: ['last 3 versions']})];