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

Update Express Payments Loading UI #4228

Merged
merged 32 commits into from
Jun 16, 2021
Merged

Conversation

mikejolley
Copy link
Member

This PR makes some improvements to the Express Payments area and related components, notably:

  • Split out the loading spinner from our button component so it can be used in the loading mask. This means we use one style of spinner animation instead of 2 :)
  • Fixed LoadingMask to:
    • a) Block mouse interaction when masked
    • b) Not force children to re-render if loading state changes
  • useShallowEqual now returns a default value instead of undefined. Updated typescript hints to match.
  • Added loading mask to the express payments area
  • Prevented excessive re-renders in express payments area by adding memorization to it's dependencies

Fixes #4159

How to test the changes in this Pull Request:

To test this properly you'll need Stripe setup locally in sandbox mode. You can test the express payments with Chrome/Edge Pay, or Apple Pay in Safari.

  1. Add an item to the cart
  2. Go to checkout and wait for express payment methods to display
  3. Toggle to change shipping; express payments should show the loading spinner and be blocked until the request is complete
  4. Pay using an express payment method
  5. After clicking Pay, the express payments area will show a loading spinner. When the request is complete and the checkout starts to redirect, the express payments area will remain blocked (no spinner).

Changelog

Show loading state in the express payments area whilst payment is processing or the page is redirecting.

@mikejolley mikejolley self-assigned this May 18, 2021
@mikejolley mikejolley requested a review from a team as a code owner May 18, 2021 17:01
@mikejolley mikejolley requested review from frontdevde and removed request for a team May 18, 2021 17:01
@github-actions
Copy link
Contributor

github-actions bot commented May 18, 2021

Size Change: +2.8 kB (0%)

Total Size: 991 kB

Filename Size Change
build/active-filters-frontend.js 7.97 kB -6 B (0%)
build/active-filters.js 7.52 kB -8 B (0%)
build/all-products-frontend.js 34.8 kB +9 B (0%)
build/all-products.js 36.6 kB +25 B (0%)
build/all-reviews.js 9.29 kB +1 B (0%)
build/atomic-block-components/add-to-cart--atomic-block-components/button--atomic-block-components/image---a7e2bb9b.js 2.41 kB +20 B (+1%)
build/atomic-block-components/add-to-cart--atomic-block-components/image--atomic-block-components/title.js 335 B +1 B (0%)
build/atomic-block-components/add-to-cart-frontend.js 8.75 kB -1 B (0%)
build/atomic-block-components/add-to-cart.js 7.8 kB -9 B (0%)
build/atomic-block-components/button.js 843 B -1 B (0%)
build/atomic-block-components/category-list-frontend.js 470 B +1 B (0%)
build/atomic-block-components/category-list.js 477 B -1 B (0%)
build/atomic-block-components/image-frontend.js 1.65 kB -3 B (0%)
build/atomic-block-components/image.js 1.31 kB +2 B (0%)
build/atomic-block-components/price-frontend.js 1.98 kB +2 B (0%)
build/atomic-block-components/price.js 2.01 kB +1 B (0%)
build/atomic-block-components/rating.js 523 B -2 B (0%)
build/atomic-block-components/sale-badge.js 473 B -2 B (0%)
build/atomic-block-components/stock-indicator-frontend.js 570 B +1 B (0%)
build/atomic-block-components/summary.js 909 B -1 B (0%)
build/atomic-block-components/tag-list-frontend.js 466 B +2 B (0%)
build/atomic-block-components/tag-list.js 471 B -1 B (0%)
build/atomic-block-components/title-frontend.js 1.41 kB -1 B (0%)
build/atomic-block-components/title.js 1.26 kB -1 B (0%)
build/attribute-filter-frontend.js 17.7 kB +1 B (0%)
build/attribute-filter.js 11.4 kB -4 B (0%)
build/blocks-checkout-editor.js 10.5 kB +18 B (0%)
build/blocks-checkout.js 19.9 kB +15 B (0%)
build/cart-frontend.js 78.4 kB +434 B (+1%)
build/cart.js 45.1 kB +727 B (+2%)
build/checkout-frontend.js 82.4 kB +469 B (+1%)
build/checkout.js 47.3 kB +618 B (+1%)
build/featured-category.js 7.24 kB +8 B (0%)
build/featured-product.js 9.4 kB +3 B (0%)
build/handpicked-products.js 5.89 kB +2 B (0%)
build/price-filter-frontend.js 14.2 kB -17 B (0%)
build/price-filter.js 9.3 kB -1 B (0%)
build/product-best-sellers.js 6.12 kB -1 B (0%)
build/product-categories.js 3.39 kB +7 B (0%)
build/product-category.js 7 kB +4 B (0%)
build/product-new.js 6.29 kB +6 B (0%)
build/product-on-sale.js 6.62 kB +2 B (0%)
build/product-search.js 2.68 kB -1 B (0%)
build/product-tag.js 6.11 kB +3 B (0%)
build/product-top-rated.js 6.25 kB -5 B (0%)
build/products-by-attribute.js 7.22 kB -1 B (0%)
build/reviews-by-product.js 12.7 kB +2 B (0%)
build/reviews-frontend.js 8.94 kB -3 B (0%)
build/single-product-frontend.js 38.2 kB +18 B (0%)
build/single-product.js 9.65 kB +2 B (0%)
build/wc-blocks-registry.js 2.75 kB -1 B (0%)
build/wc-blocks-shared-hocs.js 1.74 kB +1 B (0%)
build/wc-blocks-style-rtl.css 19.1 kB +110 B (+1%)
build/wc-blocks-style.css 19 kB +112 B (+1%)
build/wc-blocks-vendors.js 242 kB +245 B (0%)
build/wc-blocks.js 3.51 kB -2 B (0%)
build/wc-settings.js 2.94 kB -1 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/atomic-block-components/add-to-cart--atomic-block-components/button.js 2 kB 0 B
build/atomic-block-components/button-frontend.js 1.73 kB 0 B
build/atomic-block-components/rating-frontend.js 521 B 0 B
build/atomic-block-components/sale-badge-frontend.js 469 B 0 B
build/atomic-block-components/sku-frontend.js 389 B 0 B
build/atomic-block-components/sku.js 393 B 0 B
build/atomic-block-components/stock-indicator.js 572 B 0 B
build/atomic-block-components/summary-frontend.js 906 B 0 B
build/price-format.js 1.38 kB 0 B
build/reviews-by-category.js 11.2 kB 0 B
build/vendors--atomic-block-components/price-frontend.js 6.54 kB 0 B
build/wc-blocks-data.js 10.9 kB 0 B
build/wc-blocks-editor-style-rtl.css 14.9 kB 0 B
build/wc-blocks-editor-style.css 14.9 kB 0 B
build/wc-blocks-google-analytics.js 1.99 kB 0 B
build/wc-blocks-middleware.js 1.48 kB 0 B
build/wc-blocks-shared-context.js 1.54 kB 0 B
build/wc-blocks-vendors-style-rtl.css 1.05 kB 0 B
build/wc-blocks-vendors-style.css 1.05 kB 0 B
build/wc-payment-method-bacs.js 812 B 0 B
build/wc-payment-method-cheque.js 807 B 0 B
build/wc-payment-method-cod.js 903 B 0 B
build/wc-payment-method-paypal.js 844 B 0 B
build/wc-payment-method-stripe.js 12.4 kB 0 B

compressed-size-action

Copy link
Contributor

@frontdevde frontdevde left a comment

Choose a reason for hiding this comment

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

EDIT: As of the latest rebase on May 27, I can't reproduce this issue anymore.

I'm seeing an issue with Chrome Pay on this branch that I'm not seeing on trunk. I have doubled checked this by first finding the issue on this branch. Then switching to trunk, doing a clean install I wasn't able to reproduce the issue. Then switching back to this branch doing a clean install the issue resurfaced again.

I've sent you a DM with a screen recording that I won't share here due to it containing private data. But I'll describe the issue in case we need someone else to see if they can reproduce it.

What happened

In Chrome, I click the Buy Now button. This triggers the Chrome modal for express payment. The Pay button is greyed out. I select a Delivery Address. This leads to a spinner showing in the Chrome modal for 60 seconds before the modal closes itself / crashes. I click Buy Now again, the modal reopens. The Pay button is still grey. I select a Delivery Address, this time the selection is instant. The Pay button turns blue and I can check out successfully. Now the payment is registered in the Stripe backend.

What should've happened
The modal shouldn't have closed itself. Instead, the selection should've updated and the Pay button should've become active.

@mikejolley mikejolley force-pushed the update/express-payments-loading-ui branch from 226cd43 to 135ec39 Compare May 27, 2021 10:25
Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

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

I'll still do some testing (I haven't yet) but I did see one potential spot that could result in issues (depending on what else triggers re-rendering in the stack).

Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

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

Testing isn't going too well for me in Edge using the Browser pay modal:

When there is a shopper cached in the browser instance (i.e. address details etc will get filled in on loading the checkout). I get an error when setting the shipping address in the browser pay modal. I've verified the same conditions do not produce an error in trunk. I highly suspect this is due to how we're passing through the paymentMethodInterface data. Under the right conditions the data is stale.

CleanShot 2021-05-28 at 07 52 57

The other issue is that this isn't adding a loading transition or preventing the express payment button from being clicked again when a card requiring 3DS confirmation is used:

CleanShot 2021-05-28 at 07 56 21

@github-actions
Copy link
Contributor

Payment method content could potentially become a bottlen...

Find a way to Memorize Express Payment Method Content

Payment method content could potentially become a bottleneck if lots of logic is ran in the content component. ItCurrently re-renders excessively but is not easy to useMemo because paymentMethodInterface could become stale.paymentMethodInterface itself also updates on most renders.


https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/5ba2c97143fc34f1f0237096261b8255b9dc7133/assets/js/blocks/cart-checkout/payment-methods/express-payment-methods.js#L61-L76

🚀 This comment was generated by the automations bot based on a todo comment in 5ba2c97 in #4228. cc @mikejolley

@mikejolley mikejolley removed their assignment May 28, 2021
@mikejolley
Copy link
Member Author

I removed the useMemo and logged a todo to fix the error some people encountered during payment.

The other issue, 3D Secure in latest Stripe, is not resolved. I added loading state for waitingForRedirect status but didn't realise 3DS has it's own process. Whilst the 3DS modal is active, the status is idle, so we have no way to track it currently. We need a new status to solve this.

I've moved this back in progress and removed my assignment. Ill pick it up again next week if no-one gets to it before I'm back from AFK.

@mikejolley mikejolley force-pushed the update/express-payments-loading-ui branch from 28a2809 to 80203c9 Compare June 11, 2021 13:00
Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

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

Wow, there's some really nice refactoring in here (really like moving logic out of the payment method context data provider into custom hooks...makes the code easier to read) 👏🏻

Summary of feedback sprinkled through inline comments:

  • Some concerns around change in html structure (as surfaced in test snapshots as well) and how that may impact existing styling. Likely not a problem but still had questions.
  • A question around loading interactivity restrictions.
  • A requested change around unnecessary use of useShallowEqual for boolean values.
  • Maybe add some testing notes (or even better, automated tests) to cover change to dispatchCheckoutEvent.
  • Might be some opportunity to clean up the overlap between isDoingExpressPayment and expressPaymentMethodActive (but could be done in a followup).

Besides the above, regarding onExpressPaymentError:

  • I like the implementation and think it works well.
  • I'm not sure on the name onExpressPaymentError. I think it shares similarity to the naming structure for other event subscribers but itself isn't a subscriber function.
  • I would like to see setExpressPaymentError as the name of the new method and you move in the setting of notices into the new method. I don't think we need to have two separate behaviours here.

I did very basic testing of Stripe (with your branch for the gateway) and as far as I can tell it works great! However, I'm really leaning on your own testing for complete validation of payment processing flow.

I do plan on trying to setup WooCommerce Payments and test with it too before giving the ✅ .

Comment on lines 49 to 54
doAction(
`experimental__woocommerce_blocks-checkout-${ eventName }`,
{
...eventParams,
storeCart,
cartParam,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a significant change and testing notes don't cover it (and afaict there are no automated tests covering this - although I could be mistaken). Should there be some mention of coverage in testing notes and/or add some automated tests around behaviour?

I do see how this improves the memoization of dispatchCheckoutEvent given the high volatility of store cart value. On the surface this seems like there shouldn't be any unintended side-effects of the change given cartParam should always reflect the current store state when dispatchCheckoutEvent is invoked.

Copy link
Member Author

@mikejolley mikejolley Jun 15, 2021

Choose a reason for hiding this comment

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

I know why I did this (it's used as a dependency in various places and I didn't want to trigger the effects when the cart changed).

The change was to store the cart as a ref so the callback function can use the ref instead of using cart as a dependency. I did a quick test to make sure I understand refs and callbacks so it will have the latest cart object available (https://codesandbox.io/s/clever-brook-i8fuy?file=/src/app.js).

I know tests would be good, but I'm still not confident writing tests from scratch and I know it will take me the rest of the week to fathom.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've restored the storeCart parameter name here; that was an oversight on my part. It's still using the ref.

Copy link
Contributor

Choose a reason for hiding this comment

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

I know tests would be good, but I'm still not confident writing tests from scratch and I know it will take me the rest of the week to fathom.

I'm okay with it not blocking the PR, but I do think it'd be worthwhile doing this as a followup. We need more confidence when changing things and automated tests are a good way to get that.

@github-actions
Copy link
Contributor

Find a way to block buttons/form components when LoadingM...

Find a way to block buttons/form components when LoadingMask isLoading


https://github.com/woocommerce/woocommerce-gutenberg-products-block/blob/b8ba959ee00eb6f58c424aca063f6d682d9dd242/assets/js/base/components/loading-mask/index.js#L14-L17

🚀 This comment was generated by the automations bot based on a todo comment in b8ba959 in #4228. cc @mikejolley

Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

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

Looks great Mike! I tested this with WooCommerce Payments and things check out there.

The only remaining issue I noticed is that when the express payment method is processing after completing the 3DS verification, the "Place Order" button is still clickable (and it's possible to edit fields in the checkout. The only thing blocked is the express payment method area. We probably should be blocking the entire checkout?

Up to you whether you handle this in a followup or not but I do think it'll need addressed. Pre-approving pending your decision about the last point.

Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

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

I tested the latest changes with WooCommerce Payments, Stripe Extension, built in blocks stripe integration and also with the Stripe Extension onClose fix branch. I tested GooglePay (via chrome) and Browser Payment Request (via Edge). As far as I could tell most behaviour works okay.

However, in my testing I am seeing some issues with switching shipping address in the express payment method and that rates being updated. While I'm reproducing this in trunk, it does give me pause to rely on just my testing for this. My setup is:

  • display taxes inclusive in Cart and Checkout
  • Different shipping zones with different rates based on address and postal code.
  • When I trigger GooglePay, I'll either won't get all the expected rate options initially (and a different rate selected) or I will get the expected rate options but switching the address in Google Pay does not update the rates as expected.
  • Rates in the Browser Pay modal update just fine.
  • I'm reproducing this for both built-in Stripe integration in blocks AND the Stripe extension integration. Further, with the Stripe extension I'm also reproducing on the shortcode flows.

So it'd be good to have someone else test and:

  • see if you can reproduce what I experienced above in this branch.
  • If yes to the above, see if you can reproduce in trunk too. If yes, then not a blocker to merging this PR.
  • If you can't reproduce my issue at all, then let me know, we'll have to sync up on what might be in my configuration that's triggering it (or perhaps I have something borked on my db).

I'm pre-approving because other than the above issue, things test well. I do think additional testing to verify whether the above issue I've reported is valid or not though.

@mikejolley
Copy link
Member Author

@nerrad I seem to only have issues when using this branch and Stripe Trunk. I see matching behaviour between trunk and this branch when using my Stripe onClose handling Branch. Does this line up with what you are seeing?

In the broken setup, there is no initial shipping address.

For the working setup, I get an initial rate, and can rate switch, and paying has the correct amount, but I do see the wrong amount on the checkout in the background.

@mikejolley
Copy link
Member Author

@nerrad I got it working by adding useCallback - those functions were not stable so it must have been upsetting google pay. Fixed now. I get shipping in Google Pay, but it won't accept test cards to run through the full flow.

Check your side?

@nerrad
Copy link
Contributor

nerrad commented Jun 16, 2021

Just leaving a comment here to note that Mike and I did some synchronous testing together to validate and clear the issues I reported. All was good.

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

Successfully merging this pull request may close these issues.

Block checkout and cart block UI while express payment method is processing
4 participants