This repository has been archived by the owner on Feb 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 219
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Mini Cart block in experimental builds (#4510)
* Create MiniCart block prototype * Use window.onload instead of DOMContentLoaded * Don't load translations for scripts without localized strings * Don't try to load cart instance in API requests * Remove PHP pre-rendering * Fix some typos * Move Mini Cart block files under 'cart-checkout' directory * Update selectors to follow guidelines * Only enable the MiniCart block in experimental builds * Fix wrong translations element selector * Improve lazyLoadScript and preloadScript documentation * Move lazy-load-script and preload-script to base-utils * Add function to check if script tag is already in the DOM
- Loading branch information
Showing
13 changed files
with
650 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
interface lazyLoadScriptParams { | ||
handle: string; | ||
src: string; | ||
version?: string; | ||
after?: string; | ||
before?: string; | ||
translations?: string; | ||
} | ||
|
||
/** | ||
* In WP, registered scripts are loaded into the page with an element like this: | ||
* `<script src='...' id='[SCRIPT_ID]'></script>` | ||
* This function checks whether an element matching that selector exists. | ||
* Useful to know if a script has already been appended to the page. | ||
*/ | ||
const isScriptTagInDOM = ( scriptId: string ): boolean => { | ||
const scriptElements = document.querySelectorAll( `script#${ scriptId }` ); | ||
return scriptElements.length > 0; | ||
}; | ||
|
||
/** | ||
* Appends a `<script>` tag to the document body based on the src and handle | ||
* parameters. In addition, it appends additional script tags to load the code | ||
* needed for translations and any before and after inline scripts. See these | ||
* documentation pages for more information: | ||
* | ||
* https://developer.wordpress.org/reference/functions/wp_set_script_translations/ | ||
* https://developer.wordpress.org/reference/functions/wp_add_inline_script/ | ||
*/ | ||
const lazyLoadScript = ( { | ||
handle, | ||
src, | ||
version, | ||
after, | ||
before, | ||
translations, | ||
}: lazyLoadScriptParams ): Promise< void > => { | ||
return new Promise( ( resolve, reject ) => { | ||
// Append script translations if they doesn't exist yet in the page. | ||
if ( | ||
translations && | ||
! isScriptTagInDOM( `${ handle }-js-translations` ) | ||
) { | ||
const handleTranslations = document.createElement( 'script' ); | ||
handleTranslations.innerHTML = translations; | ||
handleTranslations.id = `${ handle }-js-translations`; | ||
document.body.appendChild( handleTranslations ); | ||
} | ||
// Append before inline script if it doesn't exist yet in the page. | ||
if ( before && ! isScriptTagInDOM( `${ handle }-js-before` ) ) { | ||
const handleBeforeScript = document.createElement( 'script' ); | ||
handleBeforeScript.innerHTML = before; | ||
handleBeforeScript.id = `${ handle }-js-before`; | ||
document.body.appendChild( handleBeforeScript ); | ||
} | ||
|
||
if ( isScriptTagInDOM( `${ handle }-js` ) ) { | ||
resolve(); | ||
} else { | ||
// Append script. | ||
const handleScript = document.createElement( 'script' ); | ||
handleScript.src = version ? `${ src }?${ version }` : src; | ||
handleScript.id = `${ handle }-js`; | ||
handleScript.onerror = reject; | ||
handleScript.onload = () => { | ||
// Append after inline script if it doesn't exist yet in the page. | ||
if ( after && ! isScriptTagInDOM( `${ handle }-js-after` ) ) { | ||
const handleAfterScript = document.createElement( | ||
'script' | ||
); | ||
handleAfterScript.innerHTML = after; | ||
handleAfterScript.id = `${ handle }-js-after`; | ||
document.body.appendChild( handleAfterScript ); | ||
} | ||
resolve(); | ||
}; | ||
document.body.appendChild( handleScript ); | ||
} | ||
} ); | ||
}; | ||
|
||
export default lazyLoadScript; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
interface preloadScriptParams { | ||
handle: string; | ||
src: string; | ||
version?: string; | ||
} | ||
|
||
/** | ||
* Appends a `<link>` tag to the document head to preload a script based on the | ||
* src and handle parameters. | ||
*/ | ||
const preloadScript = ( { | ||
handle, | ||
src, | ||
version, | ||
}: preloadScriptParams ): void => { | ||
const handleScriptElements = document.querySelectorAll( | ||
`#${ handle }-js, #${ handle }-js-prefetch` | ||
); | ||
|
||
if ( handleScriptElements.length === 0 ) { | ||
const prefetchLink = document.createElement( 'link' ); | ||
prefetchLink.href = version ? `${ src }?${ version }` : src; | ||
prefetchLink.rel = 'preload'; | ||
prefetchLink.as = 'script'; | ||
prefetchLink.id = `${ handle }-js-prefetch`; | ||
document.head.appendChild( prefetchLink ); | ||
} | ||
}; | ||
|
||
export default preloadScript; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
assets/js/blocks/cart-checkout/mini-cart/component-frontend.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { renderFrontend } from '@woocommerce/base-utils'; | ||
import { useStoreCart } from '@woocommerce/base-context/hooks'; | ||
import { | ||
withStoreCartApiHydration, | ||
withRestApiHydration, | ||
} from '@woocommerce/block-hocs'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import CartLineItemsTable from '../cart/full-cart/cart-line-items-table'; | ||
|
||
const MiniCartContents = () => { | ||
const { cartItems, cartIsLoading } = useStoreCart(); | ||
|
||
if ( cartItems.length === 0 ) { | ||
return <>{ __( 'Cart is empty', 'woo-gutenberg-products-block' ) }</>; | ||
} | ||
|
||
return ( | ||
<CartLineItemsTable | ||
lineItems={ cartItems } | ||
isLoading={ cartIsLoading } | ||
/> | ||
); | ||
}; | ||
|
||
renderFrontend( { | ||
selector: '.wc-block-mini-cart__contents', | ||
Block: withStoreCartApiHydration( | ||
withRestApiHydration( MiniCartContents ) | ||
), | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { _n, sprintf } from '@wordpress/i18n'; | ||
import { useBlockProps } from '@wordpress/block-editor'; | ||
import type { ReactElement } from 'react'; | ||
|
||
const MiniCartBlock = (): ReactElement => { | ||
const blockProps = useBlockProps( { | ||
className: 'wc-block-mini-cart', | ||
} ); | ||
|
||
const productCount = 0; | ||
|
||
return ( | ||
<div { ...blockProps }> | ||
<button className="wc-block-mini-cart__button"> | ||
{ sprintf( | ||
/* translators: %d is the number of products in the cart. */ | ||
_n( | ||
'%d product', | ||
'%d products', | ||
productCount, | ||
'woo-gutenberg-products-block' | ||
), | ||
productCount | ||
) } | ||
</button> | ||
</div> | ||
); | ||
}; | ||
|
||
export default MiniCartBlock; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { getSetting } from '@woocommerce/settings'; | ||
import preloadScript from '@woocommerce/base-utils/preload-script'; | ||
import lazyLoadScript from '@woocommerce/base-utils/lazy-load-script'; | ||
|
||
interface dependencyData { | ||
src: string; | ||
version?: string; | ||
after?: string; | ||
before?: string; | ||
translations?: string; | ||
} | ||
|
||
// eslint-disable-next-line @wordpress/no-global-event-listener | ||
window.onload = () => { | ||
const miniCartBlocks = document.querySelectorAll( '.wc-block-mini-cart' ); | ||
|
||
if ( miniCartBlocks.length === 0 ) { | ||
return; | ||
} | ||
|
||
const dependencies = getSetting( | ||
'mini_cart_block_frontend_dependencies', | ||
{} | ||
) as Record< string, dependencyData >; | ||
|
||
// Preload scripts | ||
for ( const dependencyHandle in dependencies ) { | ||
const dependency = dependencies[ dependencyHandle ]; | ||
preloadScript( { | ||
handle: dependencyHandle, | ||
...dependency, | ||
} ); | ||
} | ||
|
||
miniCartBlocks.forEach( ( miniCartBlock ) => { | ||
const miniCartButton = miniCartBlock.querySelector( | ||
'.wc-block-mini-cart__button' | ||
); | ||
const miniCartContents = miniCartBlock.querySelector( | ||
'.wc-block-mini-cart__contents' | ||
); | ||
|
||
if ( ! miniCartButton || ! miniCartContents ) { | ||
// Markup is not correct, abort. | ||
return; | ||
} | ||
|
||
const showContents = async () => { | ||
miniCartContents.removeAttribute( 'hidden' ); | ||
|
||
// Load scripts | ||
for ( const dependencyHandle in dependencies ) { | ||
const dependency = dependencies[ dependencyHandle ]; | ||
await lazyLoadScript( { | ||
handle: dependencyHandle, | ||
...dependency, | ||
} ); | ||
} | ||
}; | ||
const hideContents = () => | ||
miniCartContents.setAttribute( 'hidden', 'true' ); | ||
|
||
miniCartButton.addEventListener( 'mouseover', showContents ); | ||
miniCartButton.addEventListener( 'mouseleave', hideContents ); | ||
|
||
miniCartContents.addEventListener( 'mouseover', showContents ); | ||
miniCartContents.addEventListener( 'mouseleave', hideContents ); | ||
|
||
miniCartButton.addEventListener( 'focus', showContents ); | ||
miniCartButton.addEventListener( 'blur', hideContents ); | ||
} ); | ||
}; |
Oops, something went wrong.