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

Commit 0509a6c

Browse files
authored
Open Mini Cart block when adding a product to cart via an AJAX call (#4666)
1 parent eef99bb commit 0509a6c

File tree

8 files changed

+187
-67
lines changed

8 files changed

+187
-67
lines changed

assets/js/base/components/drawer/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import './style.scss';
1212

1313
interface DrawerProps {
1414
children: JSX.Element;
15+
className?: string;
1516
isOpen: boolean;
1617
onClose: () => void;
1718
slideIn?: boolean;
@@ -21,6 +22,7 @@ interface DrawerProps {
2122

2223
const Drawer = ( {
2324
children,
25+
className,
2426
isOpen,
2527
onClose,
2628
slideIn = true,
@@ -39,7 +41,7 @@ const Drawer = ( {
3941
title={ title }
4042
focusOnMount={ true }
4143
onRequestClose={ onClose }
42-
className="wc-block-components-drawer"
44+
className={ classNames( className, 'wc-block-components-drawer' ) }
4345
overlayClassName={ classNames(
4446
'wc-block-components-drawer__screen-overlay',
4547
{

assets/js/blocks/cart-checkout/cart/full-cart/style.scss

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ table.wc-block-cart-items {
8787

8888
// Loading placeholder state.
8989
.wc-block-cart--is-loading,
90-
.wc-block-mini-cart.is-loading {
90+
.wc-block-mini-cart__drawer.is-loading {
9191
th span,
9292
h2 span {
9393
@include placeholder();
@@ -98,45 +98,34 @@ table.wc-block-cart-items {
9898
h2 span {
9999
min-width: 33%;
100100
}
101-
.wc-block-cart-item__price,
102-
.wc-block-cart-item__individual-price,
103-
.wc-block-cart-item__product-metadata,
104-
.wc-block-cart-item__image > *,
101+
.wc-block-components-product-price,
102+
.wc-block-components-product-metadata,
105103
.wc-block-components-quantity-selector {
106104
@include placeholder();
107105
}
108-
.wc-block-cart-item__product-name {
106+
.wc-block-components-product-name {
109107
@include placeholder();
110108
@include force-content();
111109
min-width: 84px;
112110
display: inline-block;
113111
}
114-
.wc-block-cart-item__product-metadata {
112+
.wc-block-components-product-metadata {
115113
margin-top: 0.25em;
116114
min-width: 8em;
117115
}
118116
.wc-block-cart-item__remove-link {
119117
visibility: hidden;
120118
}
121-
.wc-block-cart-item__image a {
119+
.wc-block-cart-item__image > a {
120+
@include placeholder();
122121
display: block;
123122
}
124-
.wc-block-cart-item__individual-price {
123+
.wc-block-components-product-price {
125124
@include force-content();
126125
max-width: 3em;
127126
display: block;
128127
margin-top: 0.25em;
129128
}
130-
.wc-block-cart-item__total {
131-
> span,
132-
> div {
133-
display: none;
134-
}
135-
.wc-block-cart-item__price {
136-
@include force-content();
137-
display: block;
138-
}
139-
}
140129
.wc-block-cart__sidebar .components-card {
141130
@include placeholder();
142131
@include force-content();

assets/js/blocks/cart-checkout/mini-cart/component-frontend.tsx

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
11
/**
22
* External dependencies
33
*/
4+
import classNames from 'classnames';
45
import { __, _n, sprintf } from '@wordpress/i18n';
5-
import { useState } from '@wordpress/element';
6-
import { renderFrontend } from '@woocommerce/base-utils';
6+
import { useState, useEffect } from '@wordpress/element';
7+
import { dispatch } from '@wordpress/data';
8+
import {
9+
translateJQueryEventToNative,
10+
renderFrontend,
11+
} from '@woocommerce/base-utils';
712
import { useStoreCart } from '@woocommerce/base-context/hooks';
813
import Drawer from '@woocommerce/base-components/drawer';
9-
import {
10-
withStoreCartApiHydration,
11-
withRestApiHydration,
12-
} from '@woocommerce/block-hocs';
14+
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
1315

1416
/**
1517
* Internal dependencies
1618
*/
1719
import CartLineItemsTable from '../cart/full-cart/cart-line-items-table';
20+
import withMiniCartConditionalHydration from './with-mini-cart-conditional-hydration';
1821
import './style.scss';
1922

20-
interface MiniCartBlock {
23+
interface MiniCartBlockProps {
2124
isPlaceholderOpen?: boolean;
2225
}
2326

24-
const MiniCartBlock = ( { isPlaceholderOpen = false } ): JSX.Element => {
27+
const MiniCartBlock = ( {
28+
isPlaceholderOpen = false,
29+
}: MiniCartBlockProps ): JSX.Element => {
2530
const { cartItems, cartItemsCount, cartIsLoading } = useStoreCart();
2631
const [ isOpen, setIsOpen ] = useState< boolean >( isPlaceholderOpen );
2732
// We already rendered the HTML drawer placeholder, so we want to skip the
@@ -30,16 +35,42 @@ const MiniCartBlock = ( { isPlaceholderOpen = false } ): JSX.Element => {
3035
isPlaceholderOpen
3136
);
3237

38+
useEffect( () => {
39+
const openMiniCartAndRefreshData = () => {
40+
dispatch( storeKey ).invalidateResolutionForStore();
41+
setSkipSlideIn( false );
42+
setIsOpen( true );
43+
};
44+
45+
// Make it so we can read jQuery events triggered by WC Core elements.
46+
const removeJQueryAddedToCartEvent = translateJQueryEventToNative(
47+
'added_to_cart',
48+
'wc-blocks_added_to_cart'
49+
);
50+
51+
document.body.addEventListener(
52+
'wc-blocks_added_to_cart',
53+
openMiniCartAndRefreshData
54+
);
55+
56+
return () => {
57+
removeJQueryAddedToCartEvent();
58+
59+
document.body.removeEventListener(
60+
'wc-blocks_added_to_cart',
61+
openMiniCartAndRefreshData
62+
);
63+
};
64+
}, [] );
65+
3366
const contents =
34-
cartItems.length === 0 ? (
67+
! cartIsLoading && cartItems.length === 0 ? (
3568
<>{ __( 'Cart is empty', 'woo-gutenberg-products-block' ) }</>
3669
) : (
37-
<div className="is-mobile">
38-
<CartLineItemsTable
39-
lineItems={ cartItems }
40-
isLoading={ cartIsLoading }
41-
/>
42-
</div>
70+
<CartLineItemsTable
71+
lineItems={ cartItems }
72+
isLoading={ cartIsLoading }
73+
/>
4374
);
4475

4576
return (
@@ -65,6 +96,13 @@ const MiniCartBlock = ( { isPlaceholderOpen = false } ): JSX.Element => {
6596
) }
6697
</button>
6798
<Drawer
99+
className={ classNames(
100+
'wc-block-mini-cart__drawer',
101+
'is-mobile',
102+
{
103+
'is-loading': cartIsLoading,
104+
}
105+
) }
68106
title={ sprintf(
69107
/* translators: %d is the count of items in the cart. */
70108
_n(
@@ -105,10 +143,9 @@ const renderMiniCartFrontend = () => {
105143

106144
renderFrontend( {
107145
selector: '.wc-block-mini-cart',
108-
Block: withStoreCartApiHydration(
109-
withRestApiHydration( MiniCartBlock )
110-
),
146+
Block: withMiniCartConditionalHydration( MiniCartBlock ),
111147
getProps: ( el: HTMLElement ) => ( {
148+
isDataOutdated: el.dataset.isDataOutdated,
112149
isPlaceholderOpen: el.dataset.isPlaceholderOpen,
113150
} ),
114151
} );

assets/js/blocks/cart-checkout/mini-cart/frontend.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { getSetting } from '@woocommerce/settings';
55
import preloadScript from '@woocommerce/base-utils/preload-script';
66
import lazyLoadScript from '@woocommerce/base-utils/lazy-load-script';
7+
import { translateJQueryEventToNative } from '@woocommerce/base-utils/legacy-events';
78

89
interface dependencyData {
910
src: string;
@@ -16,6 +17,7 @@ interface dependencyData {
1617
// eslint-disable-next-line @wordpress/no-global-event-listener
1718
window.onload = () => {
1819
const miniCartBlocks = document.querySelectorAll( '.wc-block-mini-cart' );
20+
let wasLoadScriptsCalled = false;
1921

2022
if ( miniCartBlocks.length === 0 ) {
2123
return;
@@ -35,7 +37,31 @@ window.onload = () => {
3537
} );
3638
}
3739

40+
// Make it so we can read jQuery events triggered by WC Core elements.
41+
const removeJQueryAddingToCartEvent = translateJQueryEventToNative(
42+
'adding_to_cart',
43+
'wc-blocks_adding_to_cart'
44+
);
45+
const removeJQueryAddedToCartEvent = translateJQueryEventToNative(
46+
'added_to_cart',
47+
'wc-blocks_added_to_cart'
48+
);
49+
3850
const loadScripts = async () => {
51+
// Ensure we only call loadScripts once.
52+
if ( wasLoadScriptsCalled ) {
53+
return;
54+
}
55+
wasLoadScriptsCalled = true;
56+
57+
// Remove adding to cart event handler.
58+
document.body.removeEventListener(
59+
'wc-blocks_adding_to_cart',
60+
loadScripts
61+
);
62+
removeJQueryAddingToCartEvent();
63+
64+
// Lazy load scripts.
3965
for ( const dependencyHandle in dependencies ) {
4066
const dependency = dependencies[ dependencyHandle ];
4167
await lazyLoadScript( {
@@ -45,7 +71,9 @@ window.onload = () => {
4571
}
4672
};
4773

48-
miniCartBlocks.forEach( ( miniCartBlock ) => {
74+
document.body.addEventListener( 'wc-blocks_adding_to_cart', loadScripts );
75+
76+
miniCartBlocks.forEach( ( miniCartBlock, i ) => {
4977
if ( ! ( miniCartBlock instanceof HTMLElement ) ) {
5078
return;
5179
}
@@ -63,17 +91,37 @@ window.onload = () => {
6391
}
6492

6593
const showContents = () => {
94+
document.body.removeEventListener(
95+
'wc-blocks_added_to_cart',
96+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
97+
showContentsAndUpdate
98+
);
6699
miniCartBlock.dataset.isPlaceholderOpen = 'true';
67100
miniCartDrawerPlaceholderOverlay.classList.add(
68101
'wc-block-components-drawer__screen-overlay--with-slide-in'
69102
);
70103
miniCartDrawerPlaceholderOverlay.classList.remove(
71104
'wc-block-components-drawer__screen-overlay--is-hidden'
72105
);
106+
removeJQueryAddedToCartEvent();
107+
};
108+
109+
const showContentsAndUpdate = () => {
110+
miniCartBlock.dataset.isDataOutdated = 'true';
111+
showContents();
73112
};
74113

75114
miniCartButton.addEventListener( 'mouseover', loadScripts );
76115
miniCartButton.addEventListener( 'focus', loadScripts );
77116
miniCartButton.addEventListener( 'click', showContents );
117+
118+
// There might be more than one Mini Cart block in the page. Make sure
119+
// only one opens when adding a product to the cart.
120+
if ( i === 0 ) {
121+
document.body.addEventListener(
122+
'wc-blocks_added_to_cart',
123+
showContentsAndUpdate
124+
);
125+
}
78126
} );
79127
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
.modal-open .wc-block-mini-cart__button {
22
pointer-events: none;
33
}
4+
5+
// Reset font size so it doesn't depend on drawer's ancestors.
6+
.wc-block-mini-cart__drawer {
7+
font-size: 1rem;
8+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import {
5+
withStoreCartApiHydration,
6+
withRestApiHydration,
7+
} from '@woocommerce/block-hocs';
8+
9+
interface MiniCartBlockInterface {
10+
// Signals whether the cart data is outdated. That happens when
11+
// opening the mini cart after adding a product to the cart.
12+
isDataOutdated?: boolean;
13+
// Signals that the HTML placeholder drawer has been opened. Needed
14+
// to know whether we have to skip the slide in animation.
15+
isPlaceholderOpen?: boolean;
16+
}
17+
18+
// Custom HOC to conditionally hydrate API data depending on the isDataOutdated
19+
// prop.
20+
export default (
21+
OriginalComponent: ( component: MiniCartBlockInterface ) => JSX.Element
22+
) => {
23+
return ( {
24+
isDataOutdated,
25+
...props
26+
}: MiniCartBlockInterface ): JSX.Element => {
27+
const Component = isDataOutdated
28+
? OriginalComponent
29+
: withStoreCartApiHydration(
30+
withRestApiHydration( OriginalComponent )
31+
);
32+
return <Component { ...props } />;
33+
};
34+
};

0 commit comments

Comments
 (0)