Skip to content

Commit debc00c

Browse files
committed
Add validation error handling
1 parent 69d529d commit debc00c

File tree

6 files changed

+150
-6
lines changed

6 files changed

+150
-6
lines changed

package-lock.json

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"prop-types": "^15.7.2",
2020
"react": "17.0.2",
2121
"react-dom": "17.0.2",
22-
"sass": "^1.38.0"
22+
"sass": "^1.38.0",
23+
"validator": "^13.7.0"
2324
},
2425
"devDependencies": {
2526
"postcss-flexbugs-fixes": "^5.0.2",

src/components/checkout/checkout-form.js

+30-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import cx from 'classnames'
33

44
import YourOrder from './your-order';
55
import PaymentModes from "./payment-modes";
6-
// import validateAndSanitizeCheckoutForm from '../../validator/checkout';
6+
import validateAndSanitizeCheckoutForm from '../../validator/checkout';
77
// import {getFormattedCart, createCheckoutData,} from "../../functions";
88
import OrderSuccess from "./order-success";
99
import Address from "./user-address";
@@ -76,15 +76,42 @@ const CheckoutForm = ({countriesData}) => {
7676
const [isStripeOrderProcessing, setIsStripeOrderProcessing] = useState(false);
7777
const [createdOrderData, setCreatedOrderData] = useState({});
7878

79-
/*
79+
/**
8080
* Handle form submit.
8181
*
8282
* @param {Object} event Event Object.
8383
*
84-
* @return {void}
84+
* @return Null.
8585
*/
8686
const handleFormSubmit = async (event) => {
8787
event.preventDefault();
88+
89+
/**
90+
* Validate Billing and Shipping Details
91+
*
92+
* Note:
93+
* 1. If billing is different than shipping address, only then validate billing.
94+
* 2. We are passing theBillingStates?.length and theShippingStates?.length, so that
95+
* the respective states should only be mandatory, if a country has states.
96+
*/
97+
const billingValidationResult = input?.billingDifferentThanShipping ? validateAndSanitizeCheckoutForm(input?.billing, theBillingStates?.length) : {errors: null, isValid: true};
98+
const shippingValidationResult = validateAndSanitizeCheckoutForm(input?.shipping, theShippingStates?.length);
99+
100+
setInput( {
101+
...input,
102+
billing: { ...input.billing, errors: billingValidationResult.errors },
103+
shipping: { ...input.shipping, errors: shippingValidationResult.errors },
104+
} );
105+
106+
// If there are any errors, return.
107+
if ( ! shippingValidationResult.isValid || ! billingValidationResult.isValid ) {
108+
return null;
109+
}
110+
111+
console.log( 'input', input );
112+
113+
setRequestError(null);
114+
88115
};
89116

90117
/*

src/components/checkout/form-elements/input-field.js

-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ const InputField = ({ handleOnChange, inputValue, name, type, label, errors, pla
66

77
const inputId = `${name}-${isShipping ? 'shipping' : ''}`;
88

9-
console.log( 'inputValue', inputValue );
10-
119
return (
1210
<div className={containerClassNames}>
1311
<label className="leading-7 text-sm text-gray-700" htmlFor={inputId}>

src/validator/checkout.js

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import validator from 'validator';
2+
import isEmpty from './is-empty';
3+
4+
5+
const validateAndSanitizeCheckoutForm = ( data, hasStates = true ) => {
6+
7+
let errors = {};
8+
let sanitizedData = {};
9+
10+
/**
11+
* Set the firstName value equal to an empty string if user has not entered the firstName, otherwise the Validator.isEmpty() wont work down below.
12+
* Note that the isEmpty() here is our custom function defined in is-empty.js and
13+
* Validator.isEmpty() down below comes from validator library.
14+
* Similarly we do it for for the rest of the fields
15+
*/
16+
data.firstName = ( ! isEmpty( data.firstName ) ) ? data.firstName : '';
17+
data.lastName = ( ! isEmpty( data.lastName ) ) ? data.lastName : '';
18+
data.company = ( ! isEmpty( data.company ) ) ? data.company : '';
19+
data.country = ( ! isEmpty( data.country ) ) ? data.country : '';
20+
data.address1 = ( ! isEmpty( data.address1 ) ) ? data.address1 : '';
21+
data.address2 = ( ! isEmpty( data.address2 ) ) ? data.address2 : '';
22+
data.city = ( ! isEmpty( data.city ) ) ? data.city : '';
23+
data.state = ( ! isEmpty( data.state ) ) ? data.state : '';
24+
data.postcode = ( ! isEmpty( data.postcode ) ) ? data.postcode : '';
25+
data.phone = ( ! isEmpty( data.phone ) ) ? data.phone : '';
26+
data.email = ( ! isEmpty( data.email ) ) ? data.email : '';
27+
data.createAccount = ( ! isEmpty( data.createAccount ) ) ? data.createAccount : '';
28+
data.orderNotes = ( ! isEmpty( data.orderNotes ) ) ? data.orderNotes : '';
29+
// data.paymentMethod = ( ! isEmpty( data.paymentMethod ) ) ? data.paymentMethod : '';
30+
31+
/**
32+
* Checks for error if required is true
33+
* and adds Error and Sanitized data to the errors and sanitizedData object
34+
*
35+
* @param {String} fieldName Field name e.g. First name, last name
36+
* @param {String} errorContent Error Content to be used in showing error e.g. First Name, Last Name
37+
* @param {Integer} min Minimum characters required
38+
* @param {Integer} max Maximum characters required
39+
* @param {String} type Type e.g. email, phone etc.
40+
* @param {boolean} required Required if required is passed as false, it will not validate error and just do sanitization.
41+
*/
42+
const addErrorAndSanitizedData = ( fieldName, errorContent, min, max, type = '', required ) => {
43+
44+
/**
45+
* Please note that this isEmpty() belongs to validator and not our custom function defined above.
46+
*
47+
* Check for error and if there is no error then sanitize data.
48+
*/
49+
if ( ! validator.isLength( data[ fieldName ], { min, max } ) ){
50+
console.log( 'fieldName', fieldName, errorContent );
51+
errors[ fieldName ] = `${errorContent} must be ${min} to ${max} characters`;
52+
}
53+
54+
if ( 'email' === type && ! validator.isEmail( data[ fieldName ] ) ){
55+
errors[ fieldName ] = `${errorContent} is not valid`;
56+
}
57+
58+
if ( 'phone' === type && ! validator.isMobilePhone( data[ fieldName ] ) ) {
59+
errors[ fieldName ] = `${errorContent} is not valid`;
60+
}
61+
62+
if ( required && validator.isEmpty( data[ fieldName ] ) ) {
63+
errors[ fieldName ] = `${errorContent} is required`;
64+
}
65+
66+
// If no errors
67+
if ( ! errors[ fieldName ] ) {
68+
sanitizedData[ fieldName ] = validator.trim( data[ fieldName ] );
69+
sanitizedData[ fieldName ] = ( 'email' === type ) ? validator.normalizeEmail( sanitizedData[ fieldName ] ) : sanitizedData[ fieldName ];
70+
sanitizedData[ fieldName ] = validator.escape( sanitizedData[ fieldName ] );
71+
}
72+
73+
};
74+
75+
addErrorAndSanitizedData( 'firstName', 'First name', 2, 35, 'string', true );
76+
addErrorAndSanitizedData( 'lastName', 'Last name', 2, 35, 'string', true );
77+
addErrorAndSanitizedData( 'company', 'Company Name', 0, 35, 'string', false );
78+
addErrorAndSanitizedData( 'country', 'Country name', 2, 55, 'string', true );
79+
addErrorAndSanitizedData( 'address1', 'Street address line 1', 12, 100,'string',true );
80+
addErrorAndSanitizedData( 'address2', '', 0, 254, 'string', false );
81+
addErrorAndSanitizedData( 'city', 'City field', 3, 25, 'string', true );
82+
addErrorAndSanitizedData( 'state', 'State/County', 0, 254, 'string', hasStates );
83+
addErrorAndSanitizedData( 'postcode', 'Post code', 2, 10, 'postcode', true );
84+
addErrorAndSanitizedData( 'phone', 'Phone number', 10, 15, 'phone', true );
85+
addErrorAndSanitizedData( 'email', 'Email', 11, 254, 'email', true );
86+
87+
// The data.createAccount is a boolean value.
88+
sanitizedData.createAccount = data.createAccount;
89+
addErrorAndSanitizedData( 'orderNotes', '', 0, 254, 'string', false );
90+
// @TODO Payment mode error to be handled later.
91+
// addErrorAndSanitizedData( 'paymentMethod', 'Payment mode field', 2, 50, 'string', false );
92+
93+
return {
94+
sanitizedData,
95+
errors,
96+
isValid: isEmpty( errors )
97+
}
98+
};
99+
100+
export default validateAndSanitizeCheckoutForm;

src/validator/is-empty.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Returns true if the value is undefined/null/empty object/empty string.
3+
*
4+
* @param value
5+
* @return {boolean}
6+
*/
7+
const isEmpty = ( value ) =>
8+
value === undefined ||
9+
value === null ||
10+
( typeof value === 'object' && Object.keys( value ).length === 0 ) ||
11+
( typeof value === 'string' && value.trim().length === 0 );
12+
13+
export default isEmpty;

0 commit comments

Comments
 (0)