-
Notifications
You must be signed in to change notification settings - Fork 219
Add client side postcode validation #8503
Changes from 2 commits
591f4d9
4c53bb8
4dbfb7c
779a51c
fb437fd
8028cce
e1b4470
711bddd
f7c6cc1
59be85f
da07670
08a1b63
a1b0146
c754683
d9d17fb
a6077cd
b6dca6f
c543789
6db40a8
0902665
36eb53b
7945760
4b39785
370263f
c849572
de8ee24
3cc951b
c29f8b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { ValidatedTextInput } from '@woocommerce/blocks-checkout'; | ||
import { ValidatedTextInput, isPostcode } from '@woocommerce/blocks-checkout'; | ||
import { | ||
BillingCountryInput, | ||
ShippingCountryInput, | ||
|
@@ -214,6 +214,47 @@ const AddressForm = ( { | |
); | ||
} | ||
|
||
const customValidationHandler = ( | ||
inputObject: HTMLInputElement | ||
) => { | ||
if ( | ||
! isPostcode( { | ||
postcode: values.postcode, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we using I wonder if the rest of the address state is something we should pass to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really sure, I think it make sense to only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I was using
Within {
"first_name": "",
"last_name": "",
"company": "",
"address_1": "",
"address_2": "",
"city": "111",
"state": "",
"postcode": "",
"country": "",
"phone": ""
} Is that what you meant? |
||
country: values.country, | ||
nielslange marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} ) | ||
) { | ||
inputObject.setCustomValidity( | ||
__( | ||
'Please provide a valid postcode', | ||
nielslange marked this conversation as resolved.
Show resolved
Hide resolved
|
||
'woo-gutenberg-products-block' | ||
) | ||
); | ||
return false; | ||
} | ||
return true; | ||
}; | ||
|
||
if ( field.key === 'postcode' ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See my other comments. I think we can just adjust the existing |
||
return ( | ||
<ValidatedTextInput | ||
id={ `${ id }-${ field.key }` } | ||
key={ `${ id }-${ field.key }` } | ||
errorId={ errorId } | ||
value={ values.postcode } | ||
label={ field.label } | ||
required={ field.required } | ||
onChange={ ( newValue: string ) => | ||
onChange( { | ||
...values, | ||
[ field.key ]: newValue, | ||
} ) | ||
} | ||
customValidation={ customValidationHandler } | ||
errorMessage={ field.errorMessage } | ||
/> | ||
); | ||
} | ||
|
||
return ( | ||
<ValidatedTextInput | ||
key={ field.key } | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
nielslange marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* External dependencies | ||
*/ | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Converts an input's validityState to a string to display on the frontend. | ||
* | ||
* This returns custom messages for invalid/required fields. Other error types use defaults from the browser (these | ||
* could be implemented in the future but are not currently used by the block checkout). | ||
*/ | ||
const getValidityMessageForInput = ( | ||
label: string, | ||
inputElement: HTMLInputElement | ||
): string => { | ||
const { valid, customError, valueMissing, badInput, typeMismatch } = | ||
inputElement.validity; | ||
|
||
// No errors, or custom error - return early. | ||
if ( valid || customError ) { | ||
return inputElement.validationMessage; | ||
} | ||
|
||
const invalidFieldMessage = sprintf( | ||
/* translators: %s field label */ | ||
__( 'Please enter a valid %s', 'woo-gutenberg-products-block' ), | ||
label.toLowerCase() | ||
); | ||
|
||
if ( valueMissing || badInput || typeMismatch ) { | ||
return invalidFieldMessage; | ||
} | ||
|
||
return inputElement.validationMessage || invalidFieldMessage; | ||
}; | ||
|
||
export default getValidityMessageForInput; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,3 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Ensures that a given value contains a string, or throws an error. | ||
*/ | ||
export const mustContain = ( | ||
value: string, | ||
requiredValue: string | ||
): true | never => { | ||
if ( ! value.includes( requiredValue ) ) { | ||
throw Error( | ||
sprintf( | ||
/* translators: %1$s value passed to filter, %2$s : value that must be included. */ | ||
__( | ||
'Returned value must include %1$s, you passed "%2$s"', | ||
'woo-gutenberg-products-block' | ||
), | ||
requiredValue, | ||
value | ||
) | ||
); | ||
} | ||
return true; | ||
}; | ||
|
||
/** | ||
* Converts an input's validityState to a string to display on the frontend. | ||
* | ||
* This returns custom messages for invalid/required fields. Other error types use defaults from the browser (these | ||
* could be implemented in the future but are not currently used by the block checkout). | ||
*/ | ||
export const getValidityMessageForInput = ( | ||
label: string, | ||
inputElement: HTMLInputElement | ||
): string => { | ||
const { valid, customError, valueMissing, badInput, typeMismatch } = | ||
inputElement.validity; | ||
|
||
// No errors, or custom error - return early. | ||
if ( valid || customError ) { | ||
return inputElement.validationMessage; | ||
} | ||
|
||
const invalidFieldMessage = sprintf( | ||
/* translators: %s field label */ | ||
__( 'Please enter a valid %s', 'woo-gutenberg-products-block' ), | ||
label.toLowerCase() | ||
); | ||
|
||
if ( valueMissing || badInput || typeMismatch ) { | ||
return invalidFieldMessage; | ||
} | ||
|
||
return inputElement.validationMessage || invalidFieldMessage; | ||
}; | ||
export { default as mustContain } from './mustContain'; | ||
export { default as getValidityMessageForInput } from './getValidityMessageForInput'; | ||
export { default as isPostcode } from './isPostcode'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { POSTCODE_REGEXES } from 'postcode-validator/lib/cjs/postcode-regexes.js'; | ||
|
||
const getCustomRegexes = () => { | ||
nielslange marked this conversation as resolved.
Show resolved
Hide resolved
|
||
POSTCODE_REGEXES.set( 'BA', /^([7-8]{1})([0-9]{4})$/ ); | ||
POSTCODE_REGEXES.set( | ||
'GB', | ||
/^([A-Z]){1}([0-9]{1,2}|[A-Z][0-9][A-Z]|[A-Z][0-9]{2}|[A-Z][0-9]|[0-9][A-Z]){1}([ ])?([0-9][A-z]{2}){1}|BFPO(?:\s)?([0-9]{1,4})$|BFPO(c\/o[0-9]{1,3})$/i | ||
); | ||
POSTCODE_REGEXES.set( 'IN', /^[1-9]{1}[0-9]{2}\s{0,1}[0-9]{3}$/ ); | ||
POSTCODE_REGEXES.set( 'JP', /^([0-9]{3})([-]?)([0-9]{4})$/ ); | ||
POSTCODE_REGEXES.set( 'LI', /^(94[8-9][0-9])$/ ); | ||
POSTCODE_REGEXES.set( 'NL', /^([1-9][0-9]{3})(\s?)(?!SA|SD|SS)[A-Z]{2}$/i ); | ||
POSTCODE_REGEXES.set( 'SI', /^([1-9][0-9]{3})$/ ); | ||
|
||
return POSTCODE_REGEXES; | ||
}; | ||
export interface IsPostcodeProps { | ||
postcode: string; | ||
country: string; | ||
} | ||
|
||
const isPostcode = ( { postcode, country }: IsPostcodeProps ) => { | ||
nielslange marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const CUSTOM_POSTCODE_REGEXES = getCustomRegexes(); | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
return CUSTOM_POSTCODE_REGEXES.get( country )!.test( postcode ); | ||
}; | ||
|
||
export default isPostcode; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { __, sprintf } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Ensures that a given value contains a string, or throws an error. | ||
*/ | ||
const mustContain = ( value: string, requiredValue: string ): true | never => { | ||
if ( ! value.includes( requiredValue ) ) { | ||
throw Error( | ||
sprintf( | ||
/* translators: %1$s value passed to filter, %2$s : value that must be included. */ | ||
__( | ||
'Returned value must include %1$s, you passed "%2$s"', | ||
'woo-gutenberg-products-block' | ||
), | ||
requiredValue, | ||
value | ||
) | ||
); | ||
} | ||
return true; | ||
}; | ||
|
||
export default mustContain; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it makes sense for this
customValidationHandler
to live here alongside other field types. It's only used by the postcode field. If you look at theValidatedTextInput
below there is also quite a bit of duplication I feel could be refactored more cleanly.How about defining this elsewhere (outside of the
AddressForm
component/utility) and add this to the main component being returned so thatcustomValidation
is either postcode validation (if the field key ispostcode
) orundefined
for other types?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So like a general validation handler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just something to improve the convenience because if we move to modal, the local state needs to be used over the data store.