Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Add client side postcode validation #8503

Merged
merged 28 commits into from
Mar 14, 2023

Conversation

nielslange
Copy link
Member

@nielslange nielslange commented Feb 22, 2023

Fixes #7722

Currently, we're validating the postcodes in the Checkout block using the PHP-based validation of WooCommerce core. This requires sending a request to the server to validate the postcode. This PR aims to validate the postcodes client-side using TS.

Alternative PR

Note

Due to a rebasing conflict, #7945 was automatically merged into trunk. I removed the accidentally merged PR. This PR aims to add the client side postcode validation to trunk.

Testing

Automated Tests

  • Changes in this PR are covered by Automated Tests.
    • Unit tests
    • E2E tests

User Facing Testing

  1. Add a product to the cart and go to the checkout page.
  2. Select United Kingdom (UK) as country.
  3. Verify that the postcode AA9A 9AA passes the validation.
  4. Verify that the postcode 9999 999 fails the validation.
  5. Verify that it's not possible to have spaces before the postcode, .e.g. AA9A 9AA.
  6. Verify that lowercase letters, e.g. aa9A 9aa, are automatically converted to uppercase letters, e.g. AA9A 9AA.
  • Do not include in the Testing Notes

WooCommerce Visibility

  • WooCommerce Core
  • Feature plugin
  • Experimental

Changelog

Validate postcodes client-site instead of server-side.

@nielslange nielslange added type: enhancement The issue is a request for an enhancement. block: checkout Issues related to the checkout block. labels Feb 22, 2023
@nielslange nielslange self-assigned this Feb 22, 2023
@woocommercebot woocommercebot requested a review from a team February 22, 2023 08:52
@github-actions
Copy link
Contributor

github-actions bot commented Feb 22, 2023

The release ZIP for this PR is accessible via:

https://wcblocks.wpcomstaging.com/wp-content/uploads/woocommerce-gutenberg-products-block-8503.zip

Script Dependencies Report

There is no changed script dependency between this branch and trunk.

This comment was automatically generated by the ./github/compare-assets action.

TypeScript Errors Report

  • Files with errors: 489
  • Total errors: 2312

🎉 🎉 This PR does not introduce new TS errors.

comments-aggregator

@github-actions
Copy link
Contributor

github-actions bot commented Feb 22, 2023

Size Change: +3.34 kB (0%)

Total Size: 1.12 MB

Filename Size Change
build/all-products-frontend.js 11.7 kB -1 B (0%)
build/all-products.js 37.7 kB +4 B (0%)
build/blocks-checkout.js 43.9 kB +2.68 kB (+7%) 🔍
build/cart-blocks/order-summary-shipping-frontend.js 11.2 kB +92 B (+1%)
build/cart-frontend.js 29 kB -3 B (0%)
build/cart.js 47.7 kB +93 B (0%)
build/checkout-blocks/billing-address-frontend.js 4.19 kB +97 B (+2%)
build/checkout-blocks/order-summary-shipping-frontend.js 11.2 kB +95 B (+1%)
build/checkout-blocks/shipping-address-frontend.js 4.14 kB +98 B (+2%)
build/checkout-frontend.js 30.5 kB -3 B (0%)
build/checkout.js 45.3 kB +97 B (0%)
build/filter-wrapper-frontend.js 14.1 kB -2 B (0%)
build/mini-cart-component-frontend.js 28 kB -3 B (0%)
build/price-format.js 1.19 kB +1 B (0%)
build/single-product.js 10.6 kB +1 B (0%)
build/wc-blocks-data.js 21.8 kB -2 B (0%)
build/wc-blocks-middleware.js 930 B -2 B (0%)
build/wc-blocks-registry.js 3.15 kB -2 B (0%)
build/wc-blocks-shared-context.js 1.52 kB +2 B (0%)
build/wc-blocks-shared-hocs.js 1.73 kB +4 B (0%)
build/wc-blocks-style-rtl.css 26.9 kB +43 B (0%)
build/wc-blocks-style.css 26.9 kB +44 B (0%)
build/wc-settings.js 2.6 kB +2 B (0%)
ℹ️ View Unchanged
Filename Size
build/active-filters-frontend.js 7.97 kB
build/active-filters-wrapper-frontend.js 5.99 kB
build/active-filters.js 7.47 kB
build/all-reviews.js 7.65 kB
build/attribute-filter-frontend.js 22.5 kB
build/attribute-filter-wrapper--stock-filter-wrapper-frontend.js 3.35 kB
build/attribute-filter-wrapper-frontend.js 4.5 kB
build/attribute-filter.js 13.2 kB
build/breadcrumbs.js 2.04 kB
build/cart-blocks/cart-accepted-payment-methods-frontend.js 1.38 kB
build/cart-blocks/cart-cross-sells-frontend.js 253 B
build/cart-blocks/cart-cross-sells-products-frontend.js 9.75 kB
build/cart-blocks/cart-express-payment--checkout-blocks/express-payment-frontend.js 5.19 kB
build/cart-blocks/cart-express-payment-frontend.js 720 B
build/cart-blocks/cart-items-frontend.js 302 B
build/cart-blocks/cart-line-items--mini-cart-contents-block/products-table-frontend.js 5.36 kB
build/cart-blocks/cart-line-items-frontend.js 1.07 kB
build/cart-blocks/cart-order-summary-frontend.js 1.24 kB
build/cart-blocks/cart-totals-frontend.js 307 B
build/cart-blocks/empty-cart-frontend.js 346 B
build/cart-blocks/filled-cart-frontend.js 654 B
build/cart-blocks/order-summary-coupon-form-frontend.js 1.62 kB
build/cart-blocks/order-summary-discount-frontend.js 2.12 kB
build/cart-blocks/order-summary-fee-frontend.js 274 B
build/cart-blocks/order-summary-heading-frontend.js 455 B
build/cart-blocks/order-summary-subtotal-frontend.js 275 B
build/cart-blocks/order-summary-taxes-frontend.js 434 B
build/cart-blocks/proceed-to-checkout-frontend.js 1.33 kB
build/catalog-sorting.js 1.7 kB
build/checkout-blocks/actions-frontend.js 1.84 kB
build/checkout-blocks/contact-information-frontend.js 2.05 kB
build/checkout-blocks/express-payment-frontend.js 1.13 kB
build/checkout-blocks/fields-frontend.js 330 B
build/checkout-blocks/order-note-frontend.js 1.14 kB
build/checkout-blocks/order-summary-cart-items-frontend.js 3.68 kB
build/checkout-blocks/order-summary-coupon-form-frontend.js 1.79 kB
build/checkout-blocks/order-summary-discount-frontend.js 2.29 kB
build/checkout-blocks/order-summary-fee-frontend.js 277 B
build/checkout-blocks/order-summary-frontend.js 1.24 kB
build/checkout-blocks/order-summary-subtotal-frontend.js 275 B
build/checkout-blocks/order-summary-taxes-frontend.js 435 B
build/checkout-blocks/payment-frontend.js 8.43 kB
build/checkout-blocks/pickup-options-frontend.js 4.04 kB
build/checkout-blocks/shipping-method-frontend.js 2.47 kB
build/checkout-blocks/shipping-methods-frontend.js 5.26 kB
build/checkout-blocks/terms-frontend.js 1.56 kB
build/checkout-blocks/totals-frontend.js 310 B
build/customer-account.js 3.16 kB
build/featured-category.js 14 kB
build/featured-product.js 14.2 kB
build/filter-wrapper.js 2.4 kB
build/general-style-rtl.css 1.31 kB
build/general-style.css 1.31 kB
build/handpicked-products.js 7.91 kB
build/legacy-template.js 5.32 kB
build/mini-cart-contents-block/empty-cart-frontend.js 360 B
build/mini-cart-contents-block/filled-cart-frontend.js 268 B
build/mini-cart-contents-block/footer-frontend.js 2.86 kB
build/mini-cart-contents-block/items-frontend.js 237 B
build/mini-cart-contents-block/products-table-frontend.js 589 B
build/mini-cart-contents-block/shopping-button-frontend.js 572 B
build/mini-cart-contents-block/title-frontend.js 367 B
build/mini-cart-contents.js 16.6 kB
build/mini-cart-frontend.js 2.02 kB
build/mini-cart.js 4.29 kB
build/price-filter-frontend.js 13.8 kB
build/price-filter-wrapper-frontend.js 6.99 kB
build/price-filter.js 8.39 kB
build/product-add-to-cart--product-button--product-category-list--product-image--product-price--product-r--a0326d00.js 253 B
build/product-add-to-cart--product-button--product-image--product-rating--product-title.js 151 B
build/product-add-to-cart-frontend.js 6.69 kB
build/product-add-to-cart.js 8.62 kB
build/product-best-sellers.js 8.25 kB
build/product-button--product-category-list--product-image--product-price--product-rating--product-sale-b--e17c7c01.js 500 B
build/product-button--product-image--product-price--product-rating--product-sale-badge--product-title.js 262 B
build/product-button-frontend.js 2.22 kB
build/product-button.js 4.01 kB
build/product-categories.js 2.36 kB
build/product-category-list-frontend.js 1.19 kB
build/product-category-list.js 501 B
build/product-category.js 9.24 kB
build/product-image-frontend.js 2.22 kB
build/product-image.js 4.12 kB
build/product-new.js 8.25 kB
build/product-on-sale.js 8.58 kB
build/product-price-frontend.js 2.39 kB
build/product-price.js 1.64 kB
build/product-query.js 10.9 kB
build/product-rating-frontend.js 1.65 kB
build/product-rating.js 919 B
build/product-results-count.js 1.66 kB
build/product-sale-badge-frontend.js 1.45 kB
build/product-sale-badge.js 819 B
build/product-search.js 2.63 kB
build/product-sku-frontend.js 707 B
build/product-sku.js 453 B
build/product-stock-indicator-frontend.js 1.32 kB
build/product-stock-indicator.js 645 B
build/product-summary-frontend.js 1.58 kB
build/product-summary.js 920 B
build/product-tag-list-frontend.js 1.18 kB
build/product-tag-list.js 498 B
build/product-tag.js 8.74 kB
build/product-title-frontend.js 1.65 kB
build/product-title.js 3.48 kB
build/product-top-rated.js 8.49 kB
build/products-by-attribute.js 9.57 kB
build/rating-filter-frontend.js 20.8 kB
build/rating-filter-wrapper-frontend.js 5.61 kB
build/rating-filter.js 7.42 kB
build/reviews-by-category.js 11.9 kB
build/reviews-by-product.js 12.9 kB
build/reviews-frontend.js 7.13 kB
build/single-product-frontend.js 17.9 kB
build/stock-filter-frontend.js 21 kB
build/stock-filter-wrapper-frontend.js 3.15 kB
build/stock-filter.js 8.13 kB
build/store-notices.js 1.69 kB
build/vendors--attribute-filter-wrapper--cart-blocks/cart-cross-sells-products--cart-blocks/order-summary--82e4ed06-frontend.js 6.86 kB
build/vendors--attribute-filter-wrapper--rating-filter-wrapper--stock-filter-wrapper-frontend.js 7.7 kB
build/vendors--cart-blocks/cart-cross-sells-products--cart-blocks/cart-line-items--cart-blocks/cart-order--3c5fe802-frontend.js 5.26 kB
build/vendors--cart-blocks/cart-cross-sells-products--cart-blocks/order-summary-shipping--checkout-blocks--18f9376a-frontend.js 19.4 kB
build/vendors--cart-blocks/cart-cross-sells-products--product-add-to-cart-frontend.js 7.26 kB
build/vendors--cart-blocks/cart-line-items--checkout-blocks/order-summary-cart-items--mini-cart-contents---233ab542-frontend.js 3.14 kB
build/vendors--cart-blocks/order-summary-shipping--checkout-blocks/order-summary-shipping--checkout-block--24d3fc0c-frontend.js 8.24 kB
build/vendors--checkout-blocks/billing-address--checkout-blocks/shipping-address-frontend.js 5.44 kB
build/vendors--checkout-blocks/shipping-method-frontend.js 12 kB
build/wc-blocks-editor-style-rtl.css 5.82 kB
build/wc-blocks-editor-style.css 5.82 kB
build/wc-blocks-google-analytics.js 1.56 kB
build/wc-blocks-vendors-style-rtl.css 1.96 kB
build/wc-blocks-vendors-style.css 1.96 kB
build/wc-blocks-vendors.js 64.4 kB
build/wc-blocks.js 2.63 kB
build/wc-payment-method-bacs.js 816 B
build/wc-payment-method-cheque.js 811 B
build/wc-payment-method-cod.js 909 B
build/wc-payment-method-paypal.js 837 B
build/wc-shipping-method-pickup-location.js 29.7 kB
build/woo-directives-runtime.js 2.73 kB
build/woo-directives-vendors.js 7.91 kB

compressed-size-action

Copy link
Member

@mikejolley mikejolley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks functional but I have made some suggestions for code refactoring particularly in the Address Form components where we've introduced quite a bit of repetition.

@@ -214,6 +214,47 @@ const AddressForm = ( {
);
}

const customValidationHandler = (
Copy link
Member

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 the ValidatedTextInput 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 that customValidation is either postcode validation (if the field key is postcode) or undefined for other types?

Copy link
Member

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?

Copy link
Member

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.

) => {
if (
! isPostcode( {
postcode: values.postcode,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using values state because inputObject.value only gives us postcode?

I wonder if the rest of the address state is something we should pass to customValidationHandler to make this type of validation easier. cc @senadir ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure, I think it make sense to only inputObject for now, as long as we're not using the address is some comparison or event tracking then it's fine for now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using values state because inputObject.value only gives us postcode?

I was using values as the callback function for customValidation expects an HTMLInputElement as input.

I wonder if the rest of the address state is something we should pass to customValidationHandler to make this type of validation easier.

Within customValidationHandler(), values holds the following object:

{
    "first_name": "",
    "last_name": "",
    "company": "",
    "address_1": "",
    "address_2": "",
    "city": "111",
    "state": "",
    "postcode": "",
    "country": "",
    "phone": ""
}

Is that what you meant?

return true;
};

if ( field.key === 'postcode' ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my other comments. I think we can just adjust the existing ValidatedTextInput below this block of code rather than redefine it for postcode. All that changes is the customValidation.

assets/js/base/components/combobox/index.tsx Show resolved Hide resolved
packages/checkout/utils/validation/isPostcode.ts Outdated Show resolved Hide resolved
packages/checkout/utils/validation/isPostcode.ts Outdated Show resolved Hide resolved
@nielslange nielslange force-pushed the add/7722-add-client-side-postcode-validation branch from ef08d98 to 711bddd Compare February 24, 2023 08:14
Copy link

@tarunvijwani tarunvijwani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working this, @nielslange. I've added a few comments regarding the Indian postcodes. Let me know if you have any questions about it.

packages/checkout/utils/validation/isPostcode.ts Outdated Show resolved Hide resolved
packages/checkout/utils/validation/test/isPostcode.ts Outdated Show resolved Hide resolved
Copy link

@tarunvijwani tarunvijwani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works great! 🎉

@nielslange nielslange requested a review from mikejolley March 8, 2023 02:30
Comment on lines +266 to +268
field.key === 'postcode'
? newValue.trimStart().toUpperCase()
: newValue,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the input field is postcode, then remove leading spaces and convert lowercase to uppercase characters on input. This part ensures that the client-side postcode validation of WooCommerce Blocks, is in sync with the server-side postcode validation of WooCommerce.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikejolley Based on our internal discussion, I've updated the PR. Would you mind reviewing it again?

@nielslange nielslange requested a review from mikejolley March 10, 2023 06:03
@mikejolley
Copy link
Member

@nielslange formatting changes seem to work well 👍🏻 I am noticing something off however. If you fill out the entire form, valid, then change say "city" by typing a letter—do not click out of the field, just leave it. Note the push changes (shipping will update). The same is no longer happening for the postcode field for me. Can you confirm?

Copy link
Member

@mikejolley mikejolley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ok to merge now—I fixed the bug. You were using the values object for validating the postcode field, but this value is always out of date because validation runs before the change handler. I fixed it by using the value from the input itself.

c29f8b8

Test case was to use a US address, enter 90210, then remove the 0. By logging the values passed to the validation handling before my fix it would validate "90210" and push. Now it validates "9021" and does not push because validation rightly fails.

@nielslange nielslange merged commit 86595f4 into trunk Mar 14, 2023
@nielslange nielslange deleted the add/7722-add-client-side-postcode-validation branch March 14, 2023 12:37
@nielslange
Copy link
Member Author

Thanks for reviewing and applying a fix to this PR, @mikejolley! 🙌

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
block: checkout Issues related to the checkout block. type: enhancement The issue is a request for an enhancement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Client side postcode validation
4 participants