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

Display Checkout block with a notice when adding a product sold individually that was already in the cart #2854

Merged
merged 12 commits into from Aug 21, 2020

Conversation

Aljullu
Copy link
Contributor

@Aljullu Aljullu commented Jul 14, 2020

Fixes #2670.

Some changes included in this PR:

  • Notices generated before running the Checkout block logic are printed before. This ensures that when CartController calls NoticeHandler::convert_notices_to_exceptions() there are not old notices.
  • Renamed CheckoutError to CheckoutOrderError so it matches its directory name. 2278519
  • Replaced two strings to match the same strings in Core, so we reduce the effort to localize WC. 9571521

Tagging @mikejolley for review since you worked on this and will probably know if this approach makes sense or there is a better way I missed.

Screenshots

imatge

How to test the changes in this Pull Request:

There are steps to reproduce in #2670, but I think they can be simplified to:

  1. Edit a product so it can only be added once to the Cart.
    imatge
  2. Go to https://one.wordpress.test/checkout/?add-to-cart=19 (replacing one.wordpress.test with your test site URL and 19 with the ID of the product you modified).
  3. Verify the Checkout block appears correctly and the error message appears as a notice.

Changelog

Fixed a bug that was causing the Checkout block to break when adding a product sold individually that was already in the cart

@Aljullu Aljullu added type: bug The issue/PR concerns a confirmed bug. block: checkout Issues related to the checkout block. labels Jul 14, 2020
@Aljullu Aljullu added this to the 3.0.0 milestone Jul 14, 2020
@Aljullu Aljullu self-assigned this Jul 14, 2020
@github-actions
Copy link
Contributor

github-actions bot commented Jul 14, 2020

Size Change: 0 B

Total Size: 1.66 MB

ℹ️ View Unchanged
Filename Size Change
build/active-filters-frontend.js 8.76 kB 0 B
build/active-filters.js 8.8 kB 0 B
build/all-products-frontend.js 31.2 kB 0 B
build/all-products.js 35.6 kB 0 B
build/all-reviews-legacy.js 9.4 kB 0 B
build/all-reviews.js 9.73 kB 0 B
build/atomic-block-components/add-to-cart-frontend.js 8.88 kB 0 B
build/atomic-block-components/add-to-cart.js 7.46 kB 0 B
build/atomic-block-components/add-to-cart~atomic-block-components/button.js 3.17 kB 0 B
build/atomic-block-components/add-to-cart~atomic-block-components/image~atomic-block-components/title.js 335 B 0 B
build/atomic-block-components/button-frontend.js 2.02 kB 0 B
build/atomic-block-components/button.js 837 B 0 B
build/atomic-block-components/category-list-frontend.js 468 B 0 B
build/atomic-block-components/category-list.js 476 B 0 B
build/atomic-block-components/image-frontend.js 1.71 kB 0 B
build/atomic-block-components/image.js 1.15 kB 0 B
build/atomic-block-components/price-frontend.js 2.09 kB 0 B
build/atomic-block-components/price.js 2.11 kB 0 B
build/atomic-block-components/rating-frontend.js 524 B 0 B
build/atomic-block-components/rating.js 528 B 0 B
build/atomic-block-components/sale-badge-frontend.js 862 B 0 B
build/atomic-block-components/sale-badge.js 864 B 0 B
build/atomic-block-components/sku-frontend.js 389 B 0 B
build/atomic-block-components/sku.js 394 B 0 B
build/atomic-block-components/stock-indicator-frontend.js 568 B 0 B
build/atomic-block-components/stock-indicator.js 570 B 0 B
build/atomic-block-components/summary-frontend.js 918 B 0 B
build/atomic-block-components/summary.js 926 B 0 B
build/atomic-block-components/tag-list-frontend.js 466 B 0 B
build/atomic-block-components/tag-list.js 472 B 0 B
build/atomic-block-components/title-frontend.js 1.22 kB 0 B
build/atomic-block-components/title.js 1.06 kB 0 B
build/attribute-filter-frontend.js 18.1 kB 0 B
build/attribute-filter.js 12.4 kB 0 B
build/blocks-legacy.js 3.54 kB 0 B
build/blocks.js 3.54 kB 0 B
build/cart-frontend.js 66.5 kB 0 B
build/cart.js 34.9 kB 0 B
build/checkout-frontend.js 83.8 kB 0 B
build/checkout.js 40.3 kB 0 B
build/editor-legacy-rtl.css 13.8 kB 0 B
build/editor-legacy.css 13.8 kB 0 B
build/editor-rtl.css 14 kB 0 B
build/editor.css 14 kB 0 B
build/featured-category-legacy.js 7.29 kB 0 B
build/featured-category.js 7.67 kB 0 B
build/featured-product-legacy.js 9.55 kB 0 B
build/featured-product.js 9.93 kB 0 B
build/handpicked-products-legacy.js 6.95 kB 0 B
build/handpicked-products.js 7.32 kB 0 B
build/price-filter-frontend.js 14.4 kB 0 B
build/price-filter.js 10.2 kB 0 B
build/product-best-sellers-legacy.js 7.03 kB 0 B
build/product-best-sellers.js 7.39 kB 0 B
build/product-categories-legacy.js 3.23 kB 0 B
build/product-categories.js 3.23 kB 0 B
build/product-category-legacy.js 7.94 kB 0 B
build/product-category.js 8.34 kB 0 B
build/product-new-legacy.js 7.19 kB 0 B
build/product-new.js 7.55 kB 0 B
build/product-on-sale-legacy.js 7.56 kB 0 B
build/product-on-sale.js 7.94 kB 0 B
build/product-search-legacy.js 3.17 kB 0 B
build/product-search.js 3.51 kB 0 B
build/product-tag-legacy.js 6.12 kB 0 B
build/product-tag.js 6.47 kB 0 B
build/product-top-rated-legacy.js 7.16 kB 0 B
build/product-top-rated.js 7.53 kB 0 B
build/products-by-attribute-legacy.js 7.92 kB 0 B
build/products-by-attribute.js 8.27 kB 0 B
build/reviews-by-category-legacy.js 11.4 kB 0 B
build/reviews-by-category.js 11.8 kB 0 B
build/reviews-by-product-legacy.js 12.9 kB 0 B
build/reviews-by-product.js 13.3 kB 0 B
build/reviews-frontend-legacy.js 8.43 kB 0 B
build/reviews-frontend.js 9.33 kB 0 B
build/single-product-frontend.js 34 kB 0 B
build/single-product.js 10.1 kB 0 B
build/style-legacy-rtl.css 16.8 kB 0 B
build/style-legacy.css 16.8 kB 0 B
build/style-rtl.css 17.5 kB 0 B
build/style.css 17.5 kB 0 B
build/vendors-legacy.js 367 kB 0 B
build/vendors-style-legacy-rtl.css 1.03 kB 0 B
build/vendors-style-legacy.css 1.03 kB 0 B
build/vendors-style-rtl.css 1.03 kB 0 B
build/vendors-style.css 1.03 kB 0 B
build/vendors.js 416 kB 0 B
build/vendors~atomic-block-components/price-frontend.js 5.65 kB 0 B
build/wc-blocks-data.js 7.09 kB 0 B
build/wc-blocks-middleware.js 931 B 0 B
build/wc-blocks-registry.js 2.28 kB 0 B
build/wc-payment-method-bacs.js 790 B 0 B
build/wc-payment-method-cheque.js 787 B 0 B
build/wc-payment-method-cod.js 875 B 0 B
build/wc-payment-method-paypal.js 831 B 0 B
build/wc-payment-method-stripe.js 11.9 kB 0 B
build/wc-settings.js 2.14 kB 0 B
build/wc-shared-context.js 1.53 kB 0 B
build/wc-shared-hocs.js 1.66 kB 0 B

compressed-size-action

@mikejolley
Copy link
Member

@Aljullu just so I understand this, is the notice thats showing in this example coming from code unrelated to blocks? i.e. core error notices from the query string?

Would another way to solve this be to clear notices before running the checkout logic and converting notices to exceptions?

@Aljullu
Copy link
Contributor Author

Aljullu commented Jul 14, 2020

@Aljullu just so I understand this, is the notice thats showing in this example coming from code unrelated to blocks? i.e. core error notices from the query string?

Correct, that error is thrown by WC Core when a product is added to the Cart.

The issue we are facing in #2670 is that checkout data is not returned by /wc/store/checkout if accessing the Checkout page with the add-to-cart argument in the URL. Instead, the error generated by Core is returned.

Would another way to solve this be to clear notices before running the checkout logic and converting notices to exceptions?

It might, but this part of the codebase is totally new to me, so I trust you more. 😄 From my understading we are already converting notices to exceptions, right? Clearing notices before running the checkout logic would fix the issue of the Checkout data not being available, but we would not be displaying any feedback to the user that the product was not added to the cart.

@mikejolley
Copy link
Member

It's tricky because we don't want non-checkout notices to pollute the checkout. I also don't think it's desirable to this query string to be passed to the API requests (is that what is happening?)—I only see usage of the exception conversion in the API itself. Are we somehow passing the add-to-cart string to the API requests? That might be a bug?

I think the purpose of convert_notices_to_exceptions in this context is to only convert the notices thrown by it's internal logic. The notices that were there before might need discarding.

@nerrad nerrad modified the milestones: 3.0.0, 3.1.0 Jul 20, 2020
@haszari
Copy link
Member

haszari commented Jul 26, 2020

Edited description to fix issue link (was linking to #2679).

That notice has HTML tags and their content stripped. WC Core throws some errors with links, but that was not playing well with the Notice component (it has the possibility to render HTML, but it's marked as unstable).

Thanks for digging into this 👍

We could use a link in some cart error notices, e.g. "please log in to your existing account" - hit this in #2851

@Aljullu
Copy link
Contributor Author

Aljullu commented Jul 30, 2020

@mikejolley

It's tricky because we don't want non-checkout notices to pollute the checkout. I also don't think it's desirable to this query string to be passed to the API requests (is that what is happening?)—I only see usage of the exception conversion in the API itself. Are we somehow passing the add-to-cart string to the API requests? That might be a bug?

I don't think so, the initial contents of the Checkout page are hydrated directly via PHP and we don't pass any params to get_endpoint_data(), so it shouldn't have the add-to-cart string, no?

I think the purpose of convert_notices_to_exceptions in this context is to only convert the notices thrown by it's internal logic. The notices that were there before might need discarding.

If we plainly discarded them, there would be no feedback for the user that the product was not added to their cart. What I was trying to achieve with this PR is that notices thrown by internal logic are not converted to exceptions but are not discarded either, instead they would be passed in a new notice property.

@Aljullu
Copy link
Contributor Author

Aljullu commented Jul 30, 2020

@haszari

We could use a link in some cart error notices, e.g. "please log in to your existing account" - hit this in #2851

That would mean we need to define own custom message, no? The issue I see from that (if I'm understanding it right) is that all this logic is currently in WC Core, so Blocks doesn't really know what error happened.

@mikejolley
Copy link
Member

I don't think so, the initial contents of the Checkout page are hydrated directly via PHP and we don't pass any params to get_endpoint_data(), so it shouldn't have the add-to-cart string, no?

Good spot. It doesn't totally make sense to me where these notices are coming from in that case, I guess added just before that hydration occurs. If that is indeed the case, we could hydrate and clear existing notices to another property if that's easier. I defer to your judgement :)

@haszari
Copy link
Member

haszari commented Jul 30, 2020

We could use a link in some cart error notices, e.g. "please log in to your existing account" - hit this in #2851

That would mean we need to define own custom message, no? The issue I see from that (if I'm understanding it right) is that all this logic is currently in WC Core, so Blocks doesn't really know what error happened.

This comment should not block this PR at all – I was just mentioning a feature request / need that's possibly related. That being that error notices might need links. Specifically, some existing WC core error notices do contain links, and we might strip the links out when we render the notices (I'm stripping the links in #2851).

Here's a link to the related comment thread: #2851 (review)

But this is all pretty tangential (unrelated) to this PR and shouldn't block it, hopefully this clarifies things :)

@Aljullu Aljullu force-pushed the fix/2670-add-to-cart-checkout-error branch from 5234f0e to 0395a49 Compare July 31, 2020 10:44
@Aljullu Aljullu requested a review from a team as a code owner July 31, 2020 10:44
@Aljullu Aljullu force-pushed the fix/2670-add-to-cart-checkout-error branch from 0395a49 to cb65fb0 Compare July 31, 2020 10:48
@Aljullu
Copy link
Contributor Author

Aljullu commented Jul 31, 2020

Updated this PR so it should be ready for review again. I updated the first comment with details on what's changed.

This comment should not block this PR at all – I was just mentioning a feature request / need that's possibly related. That being that error notices might need links. Specifically, some existing WC core error notices do contain links, and we might strip the links out when we render the notices (I'm stripping the links in #2851).

Ah, yes. The Notice component allows having actions, but I'm not sure if that's well suited for our use-cases or we would need another solution.

In this PR specifically, I don't think it really matters because the link goes to the Cart page, but that notice will be shown in the Checkout, so I think it's better not to show it.

@haszari haszari self-requested a review August 6, 2020 00:57
Copy link
Member

@haszari haszari left a comment

Choose a reason for hiding this comment

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

Thanks for figuring this out and implementing a robust solution @Aljullu !

First up a disclaimer - I'm conflicted about this, it feels like a bit of an edge case, or more specifically a general bug, not specific to cart or checkout.

I did some testing with some add-to-cart buttons with various page urls (cart block, checkout block, a regular page, cart shortcode, checkout shortcode. I wanted to explore how this works currently to fully understand the issue.

Here's what I found:

  • If the destination is a regular page, the notice is not shown on the page. This feels like a bug, or more likely, is an edge-case (indicating that this is not typical setup).
  • If you click the Add to cart button multiple times, and the destination is a page, multiple notices stack up behind the scenes.
  • When a cart or checkout shortcode page is eventually viewed (maybe some time later), all these You cannot add notices are rendered at top. This is informative, but maybe unnecessary IMO - maybe it's more helpful to just show cart/checkout with the single item added and the customer can proceed to checkout :)

Screen Shot 2020-08-06 at 1 48 00 PM

With the cart & checkout blocks, I confirmed that this PR improves things by showing the error as a notice above checkout (but not cart).

Screen Shot 2020-08-06 at 2 10 20 PM

Long term, I'm thinking we'll provide new ways for merchants to build a landing page with an Add to cart button, and that might be the cleanest "fix" for these kinds of problems. The best time to show the You can't add error is as close to the user action as possible, so an AJAX error would be ideal.

Perhaps this PR is more of an interim solution, or polish to protect inappropriate errors from causing confusion in cart & checkout block.

With that in mind, perhaps it's less important to catch and replay ALL notices generically, and maybe simpler to catch and discard, since the notices may be from some time ago. Or catch and enqueue, so the client can opt-in to displaying notices that are relevant to it; for example, cart/checkout blocks could display a whitelist of relevant notices, and if there are duplicates, just show one copy.

Can you think of any other error notices that might "leak" into cart & checkout in this way? I'm guessing there are other ways to "trick" Woo by passing query params like add-to-cart to unusual pages.

Super tricky issue!

* @param string $text Text to remove tags from.
*/
protected function strip_tags_content( $text ) {
return preg_replace( '@<(\w+)\b.*?>.*?</\1>@si', '', $text );
Copy link
Member

Choose a reason for hiding this comment

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

Is there a library routine we can use to strip html more reliably - e.g. wp_strip_all_tags? Seems risky to implement/maintain a custom regex for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the end I changed how this is done (80568e9). Now, HTML is stripped in the frontend using a library WC Admin seems to use too. In some way I think it's more correct to let the client decide whether to display HTML markup or not.

This raises the bundle size a bit, so I'm happy for other ideas.

@@ -27,6 +27,7 @@ export const DEFAULT_STATE = {
calculatingCount: 0,
orderId: checkoutData.order_id,
customerId: checkoutData.customer_id,
notices: checkoutData.notices,
Copy link
Member

Choose a reason for hiding this comment

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

Can we make the intention/purpose of this more clear? It's not a generic notices api, as far as I understand, it's more of a special case, catching orphaned errors and allowing the client to optionally render them.

I don't have any great ideas for what to rename this ... will have a think :)

@@ -99,6 +100,14 @@ const CheckoutProcessor = () => {
setIsSuppressed( expressPaymentMethodActive );
}, [ expressPaymentMethodActive, setIsSuppressed ] );

useEffect( () => {
if ( Array.isArray( checkoutNotices ) ) {
checkoutNotices.forEach( ( notice ) => {
Copy link
Member

Choose a reason for hiding this comment

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

I think this needs a comment to clarify the special case in play here. Again, I'm struggling to come up with a good comment!

@@ -245,6 +245,8 @@
* order if one exists.
* @property {boolean} hasOrder True when the checkout has a
* draft order from the API.
* @property {string} notices Notices associated to the
Copy link
Member

Choose a reason for hiding this comment

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

This is a little misleading. As I understand it, these are errors that were encountered at some point in the past, and are now getting rendered as part of this block (but might have nothing to do with it). So they aren't necessarily associated with checkout data (though that's the mechanism that sends them to client).

@@ -106,6 +106,7 @@ Via `useCheckoutContext`, the following are exposed:
- `orderId`: The order id for the order attached to the current checkout.
- `isCart`: This is true if the cart is being viewed. Note: usage of `CheckoutProvider` will automatically set this to false. There is also a `CartProvider` that wraps children in the `ShippingDataProvider` and exposes the same api as checkout context. The `CartProvider` automatically sets `isCart` to true. This allows components that implement `useCheckoutContext` to use the same api in either the cart or checkout context but still have specific behaviour to whether `isCart` is true or not.
- `hasOrder`: This is true when orderId is truthy.
- `notices`: An array of notices or messages associated to the current checkout data. For example, errors from the server which are not blocking the purchase process but have to be shown to the user.
Copy link
Member

Choose a reason for hiding this comment

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

This a bit clearer - these notices could be anything really, they might be a general store problem/notice encountered in some previous request.

Copy link
Member

Choose a reason for hiding this comment

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

Since the notices are captured as part of hydration (only), maybe we can call them captured_hydrate_notices. Again, this is not very clear at all 😄

/**
* Hydrate the checkout block with data from the API.
*
* @param AssetDataRegistry $data_registry Data registry instance.
*/
protected function hydrate_from_api( AssetDataRegistry $data_registry ) {
// Save previous notices so we can return them in a `notices` property to
// be used in the frontend.
$previous_notices = wc_get_notices( 'error' );
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to limit this to error? I think a similar situation could possibly occur with other notice types (maybe contrived/theoretical).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed this limitation. 🙂

if ( ! $data_registry->exists( 'cartData' ) ) {
$data_registry->add( 'cartData', WC()->api->get_endpoint_data( '/wc/store/cart' ) );
}
if ( ! $data_registry->exists( 'checkoutData' ) ) {
add_filter( 'woocommerce_store_api_disable_nonce_check', '__return_true' );
$data_registry->add( 'checkoutData', WC()->api->get_endpoint_data( '/wc/store/checkout' ) );
$checkout_data = WC()->api->get_endpoint_data( '/wc/store/checkout' );
if ( is_array( $previous_notices ) && count( $previous_notices ) > 0 ) {
Copy link
Member

Choose a reason for hiding this comment

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

This code is the crux of this new feature - capture and then send any dangling/orphaned notices, to prevent them being misinterpreted in checkout processing.

If we think this is worthwhile, it might be worth refactoring the code to methods so it's clearly separated from the main checkout/cart assets logic. E.g.

// Capture notices, strip html, convert to array ready for enqueuing as script data.
$irrelevant_notice_data = capture_and_clear_notices();

// Then later, optionally include the notice data in response if useful.
$checkout_data = array_merge( $checkout_data, [ 'captured_notices' => $irrelevant_notice_data ] );

Copy link
Member

Choose a reason for hiding this comment

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

If we do this for checkout, we should do it for cart block too (and consider other cases).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we do this for checkout, we should do it for cart block too (and consider other cases).

The Cart block shouldn't be affected. The issue happened because Checkout is calling convert_notices_to_exceptions, that means any notice would prevent the Checkout block from rendering and instead an error would be shown. Cart doesn't call that method so it's not affected and it shows notices like other pages.

@haszari
Copy link
Member

haszari commented Aug 6, 2020

I grabbed this and reviewed since Mike may be out for a bit 🏖️ 👾

@haszari
Copy link
Member

haszari commented Aug 10, 2020

Tangentially related enhancement issue - ensure blocks notices support actions :) #2985

@Aljullu Aljullu force-pushed the fix/2670-add-to-cart-checkout-error branch 2 times, most recently from 262dac8 to 80568e9 Compare August 11, 2020 08:27
@Aljullu
Copy link
Contributor Author

Aljullu commented Aug 11, 2020

Love all the suggestions @haszari! I implemented most of them and I will answer some of your feedback.

First up a disclaimer - I'm conflicted about this, it feels like a bit of an edge case, or more specifically a general bug, not specific to cart or checkout.

The issue is only reproducible in the Checkout block because it calls convert_notices_to_exceptions, so previously-generated notices where making the block to break. Other blocks shouldn't be affected. I wouldn't say it's an edge case, though, it has been reported by a user and it sound to me like something that needs to be fixed.

Long term, I'm thinking we'll provide new ways for merchants to build a landing page with an Add to cart button, and that might be the cleanest "fix" for these kinds of problems. The best time to show the You can't add error is as close to the user action as possible, so an AJAX error would be ideal.

💯 However, there will still be the case of links in the form ?add_to_cart=19, in that case the backend tries to add the product to the cart on page load, so there is no way to do it in an AJAX-way.

Perhaps this PR is more of an interim solution, or polish to protect inappropriate errors from causing confusion in cart & checkout block.

Correct, I think WC should give users feedback as soon as possible without requiring a page reload. But that's not yet possible with notices generated in Core and there will still be cases in which notices are generated on page load, as described above. So we still need a system so previously-generated notices don't break our blocks.

Can you think of any other error notices that might "leak" into cart & checkout in this way?

I don't, but that's just because my WC knowledge is limited. 🙂 I'm pretty sure there must be some other ways to trigger errors or notices right before the block logic is run.


I renamed several variables with your suggestions. In the API I named it captured_notices, (I skipped hydrate because, in theory, that endpoint could be used without initial hydration). In the JS-side I named it hydratedNotices, in this case the frontend doesn't care if they were captured or not, the only important thing is that they came with hydrated data.

This PR is ready for another look.

@Aljullu Aljullu requested a review from haszari August 11, 2020 09:49
@Aljullu Aljullu force-pushed the fix/2670-add-to-cart-checkout-error branch 2 times, most recently from f9c78e3 to 54d3d6b Compare August 13, 2020 06:18
@Aljullu Aljullu force-pushed the fix/2670-add-to-cart-checkout-error branch from 54d3d6b to 7289061 Compare August 13, 2020 06:19
@Aljullu
Copy link
Contributor Author

Aljullu commented Aug 13, 2020

Thanks for the in-depth review @haszari! After digging a bit more into this, it looks like (maybe) this could be fixed with just one line. 🤦 Printing the notices before running the block logic was enough to ensure they are empty when reaching NoticeHandler::convert_notices_to_exceptions(). Can you take another look?

Click Add to cart - cart button. Button link navigates to cart block page – no notice is displayed. Cart only contains 1x Belt.

I'm still unable to reproduce this. Let me know if you can still reproduce with the last changes in this pull.

@Aljullu Aljullu requested a review from haszari August 13, 2020 07:26
Copy link
Member

@haszari haszari 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 looking great. Fantastic effort finding such a clean/lightweight fix 🏅

In my testing I still saw some quirky behaviour, but I think this might actually be a Twenty Twenty bug (maybe). I don't think this is a blocker, so I'm pre-approving, but we might need some follow up issues.

By the way – I think in my previous testing I ONLY tested Twenty Twenty. So possibly some of what I was reporting was due to that. This time I used Storefont too.

Note I also think it's useful to compare behaviour with / without blocks plugin active. I'm not certain, but it's possible that hydrate_from_api is getting called on all pages, possibly causing different notice behaviour with/without blocks plugin. (Sorry if this sounds like FUD/voodoo!)

In my test setup, I have a blog post with a bunch of "add belt to cart" buttons. Each one links to the respective url with the add-to-cart param:

  • Block-based checkout page.
  • Block-based cart page.
  • Regular site page (e.g. About or Contact).
  • Shortcode cart page.
  • Shortcode checkout page.

Screen Shot 2020-08-14 at 12 03 44 PM

With your new changes, and Storefront theme, all the cases I tested work well.

  • Clicking Buy now page button now correctly shows 1 "already in cart" notice on the page.
    • I can't get multiple "already in cart" notices stacked up behind the scenes (and then rendered on an unsuspecting checkout). I think this is because the notices are caught and rendered sooner, i.e. when the regular page is rendered.
  • All other buttons also show 1x "already in cart" notice at top.

With Twenty Twenty (1.5) ...

I'm a bit baffled by these issues. Since it's working well with Storefront, perhaps we can merge and log follow up issues for Twenty Twenty. But can you investigate to confirm that the approach is robust, i.e. confirm that it's not a fluke that Storefront is working well? Maybe test with some other quality Woo themes (e.g. Threads/Block Shop/Mathew from WCCOM marketplace, or other WPORG themes) as as first step.

Here's what I saw ..

When I click Buy now page, the notice is NOT shown (in Twenty Twenty). So this is similar issue to what I was reporting before.

Those notices stack up in the background, and then render when they get a chance. On shortcode pages, all the dups are rendered:

Screen Shot 2020-08-14 at 11 55 11 AM

Also, in the checkout block, the notice is styled left-aligned:

Screen Shot 2020-08-14 at 11 55 24 AM

@@ -185,6 +185,10 @@ protected function hydrate_customer_payment_methods( AssetDataRegistry $data_reg
* @param AssetDataRegistry $data_registry Data registry instance.
*/
protected function hydrate_from_api( AssetDataRegistry $data_registry ) {
// Print existing notices now, otherwise they are caught by the Cart
// Controller and converted to exceptions.
wc_print_notices();
Copy link
Member

Choose a reason for hiding this comment

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

👌

It's beautiful, man!

@senadir
Copy link
Member

senadir commented Aug 17, 2020

@Aljullu is this ready to be merged?

@Aljullu Aljullu modified the milestones: 3.2.0, 3.3.0 Aug 18, 2020
@Aljullu
Copy link
Contributor Author

Aljullu commented Aug 18, 2020

@Aljullu is this ready to be merged?

@senadir I changed the milestone to 3.3.0, there is not need to rush this PR IMO. This way I can do some testing with the steps shared by @haszari. Thanks for asking! 🙂

@Aljullu
Copy link
Contributor Author

Aljullu commented Aug 18, 2020

@haszari, thanks for writing the testing steps, I could reproduce what you mean.

Let me split my response in several parts:

Notices not always appearing in Twenty Twenty.

This seems to be the expected behavior. Even though Storefront displays WC notices on top of each page, that's not the case for other themes like Twenty Twenty. In them, notices are only shown when WC Core prints them. That happens in some occasions like when rendering the Cart and Checkout blocks, but not on every page load.

I think that's the cause of the confusing behavior you were experimenting: you were triggering the error several times, but since you were visiting a regular page, notices were not being printed but instead were 'accumulated'. Once you visited the Checkout shortcode, all notices appeared at once.

I'm not sure how this should affect blocks, I think there are three possibilities:

  1. We always print notices when rendering a block.
  2. Cart and Checkout block print notices, but all others don't.
  3. Only the Checkout block prints notices and all other blocks let the theme handle that (this PR).

I'm leaning towards option 1 or 2. Thoughts? In any case, I would open an issue for that since I don't think that's blocking this PR.

Should blocks use an action instead of directly calling wc_print_notices()?

In the link I posted above, you can see that pages and shortcodes in WC Core don't call wc_print_notices() directly, but instead use actions letting extensions hook into the process. Should we go for that approach too? Something like:

add_action( 'woocommerce_blocks_before_cart_block', 'woocommerce_output_all_notices', 10 );
add_action( 'woocommerce_blocks_before_checkout_block', 'woocommerce_output_all_notices', 10 );

This would mean adding a new extensibility point. While I think that would be useful for extensions, I'm a bit wary of adding actions before we have completely decided on a direction on how to implement extensibility in our blocks. So I would vote this is not blocking right now but something to keep in mind. cc @nerrad

Visual glitches

I created a PR in core to fix several visual glitches: woocommerce/woocommerce#27387.

@haszari
Copy link
Member

haszari commented Aug 18, 2020

Excellent investigation and analysis @Aljullu ! It seems to me you've hit on an area that's inconsistent across themes, and it's not clear what the "correct" behaviour is, or what Woo expects. Also we should keep in mind that the issue that triggered this is somewhat of an edge case, so the behaviour differences we're seeing might actually be low-impact.

That said, this seems like an opportunity to improve the robustness of Woo + themes 😄 (at some point - not an urgent issue IMO).

This seems to be the expected behavior. Even though Storefront displays WC notices on top of each page, that's not the case for other themes like Twenty Twenty

Aha! That does explain things. I wonder if we (woo core) should advocate for more themes to take the Storefront approach, or implement it in core as default behaviour.

I'm leaning towards option 1 or 2. Thoughts? In any case, I would open an issue for that since I don't think that's blocking this PR.

I agree this should not block this PR, and it's probably a wider (woo platform) discussion, as this is really a woo core issue that we're hitting in blocks. I could live with any those options you mention – let's see what others think. Option 3 seems the least "intrusive" to me; all options seem like a patch the symptom rather than addressing the cause.

Should blocks use an action instead of directly calling wc_print_notices()?

I agree - my instinct is that we should avoid adding hooks (or triggering existing hooks) until it's we know it's essential and needed.

@Aljullu
Copy link
Contributor Author

Aljullu commented Aug 21, 2020

I'm going to merge this as-is since it has already been approved. We can follow-up with printing (or not) notices in more blocks and extensibility in the future.

@Aljullu Aljullu merged commit 4db9fa0 into main Aug 21, 2020
@Aljullu Aljullu deleted the fix/2670-add-to-cart-checkout-error branch August 21, 2020 07:34
Aljullu added a commit that referenced this pull request Aug 24, 2020
…ld individually that was already in the cart (#2854)"

This reverts commit 4db9fa0.
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: bug The issue/PR concerns a confirmed bug.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Custom “Add to Cart” URLs - not working as expected with cart & checkout blocks
6 participants