* Pre-hydrate Mini Cart Block woocommerce/woocommerce-blocks#5882

Pre-hydrate Mini Cart Block

* try to fix test

* fix wrong behaviour
This commit is contained in:
Luigi Teschio 2022-02-18 14:44:10 +01:00 committed by GitHub
parent dceb480e00
commit 71bd69ad07
11 changed files with 159 additions and 49 deletions

View File

@ -11,11 +11,18 @@ import {
getCurrencyFromPriceResponse,
} from '@woocommerce/price-format';
import { getSettingWithCoercion } from '@woocommerce/settings';
import { isBoolean, isString } from '@woocommerce/types';
import {
CartResponseTotals,
isBoolean,
isString,
isCartResponseTotals,
isNumber,
} from '@woocommerce/types';
import {
unmountComponentAtNode,
useCallback,
useEffect,
useRef,
useState,
} from '@wordpress/element';
import { sprintf, _n } from '@wordpress/i18n';
@ -42,7 +49,20 @@ const MiniCartBlock = ( {
style,
contents = '',
}: Props ): JSX.Element => {
const { cartItemsCount, cartIsLoading, cartTotals } = useStoreCart();
const {
cartItemsCount: cartItemsCountFromApi,
cartIsLoading,
cartTotals: cartTotalsFromApi,
} = useStoreCart();
const isFirstLoadingCompleted = useRef( cartIsLoading );
useEffect( () => {
if ( isFirstLoadingCompleted.current && ! cartIsLoading ) {
isFirstLoadingCompleted.current = false;
}
}, [ cartIsLoading, isFirstLoadingCompleted ] );
const [ isOpen, setIsOpen ] = useState< boolean >( isInitiallyOpen );
// We already rendered the HTML drawer placeholder, so we want to skip the
// slide in animation.
@ -120,8 +140,29 @@ const MiniCartBlock = ( {
isBoolean
);
const preFetchedCartTotals = getSettingWithCoercion< CartResponseTotals | null >(
'cartTotals',
null,
isCartResponseTotals
);
const preFetchedCartItemsCount = getSettingWithCoercion< number >(
'cartItemsCount',
0,
isNumber
);
const taxLabel = getSettingWithCoercion( 'taxLabel', '', isString );
const cartTotals =
! isFirstLoadingCompleted.current || preFetchedCartTotals === null
? cartTotalsFromApi
: preFetchedCartTotals;
const cartItemsCount = ! isFirstLoadingCompleted.current
? cartItemsCountFromApi
: preFetchedCartItemsCount;
const subTotal = showIncludingTax
? parseInt( cartTotals.total_items, 10 ) +
parseInt( cartTotals.total_items_tax, 10 )

View File

@ -0,0 +1,3 @@
export const isBoolean = ( term: unknown ): term is boolean => {
return typeof term === 'boolean';
};

View File

@ -0,0 +1,42 @@
/**
* Internal dependencies
*/
import { CartResponseTotals } from '../type-defs';
import { isObject } from './object';
// It is the only way to create a type that contains all the object's keys and gets type-checking.
// This is useful because we want to check that the keys object ALWAYS contains all the object's keys.
// https://stackoverflow.com/questions/52028791/make-a-generic-type-arraykeyof-t-require-all-keys-of-t
type CartResponseTotalsKeys = Record< keyof CartResponseTotals, 0 >;
export const isCartResponseTotals = (
value: unknown
): value is CartResponseTotals => {
if ( ! isObject( value ) ) {
return false;
}
const keys: CartResponseTotalsKeys = {
total_items: 0,
total_items_tax: 0,
total_fees: 0,
total_fees_tax: 0,
total_discount: 0,
total_discount_tax: 0,
total_shipping: 0,
total_shipping_tax: 0,
total_price: 0,
total_tax: 0,
tax_lines: 0,
currency_code: 0,
currency_symbol: 0,
currency_minor_unit: 0,
currency_decimal_separator: 0,
currency_thousand_separator: 0,
currency_prefix: 0,
currency_suffix: 0,
};
return Object.keys( keys ).every( ( key ) => key in value );
};

View File

@ -0,0 +1,3 @@
export const isError = ( term: unknown ): term is Error => {
return term instanceof Error;
};

View File

@ -0,0 +1,6 @@
// eslint-disable-next-line @typescript-eslint/ban-types
export const isFunction = < T extends Function, U >(
term: T | U
): term is T => {
return typeof term === 'function';
};

View File

@ -1,44 +1,8 @@
export const isNull = < T >( term: T | null ): term is null => {
return term === null;
};
export const isNumber = < U >( term: number | U ): term is number => {
return typeof term === 'number';
};
export const isString = < U >( term: string | U ): term is string => {
return typeof term === 'string';
};
export const isObject = < T extends Record< string, unknown >, U >(
term: T | U
): term is NonNullable< T > => {
return (
! isNull( term ) &&
term instanceof Object &&
term.constructor === Object
);
};
export function objectHasProp< P extends PropertyKey >(
target: unknown,
property: P
): target is { [ K in P ]: unknown } {
// The `in` operator throws a `TypeError` for non-object values.
return isObject( target ) && property in target;
}
// eslint-disable-next-line @typescript-eslint/ban-types
export const isFunction = < T extends Function, U >(
term: T | U
): term is T => {
return typeof term === 'function';
};
export const isBoolean = ( term: unknown ): term is boolean => {
return typeof term === 'boolean';
};
export const isError = ( term: unknown ): term is Error => {
return term instanceof Error;
};
export * from './boolean';
export * from './cart-response-totals';
export * from './error';
export * from './function';
export * from './null';
export * from './number';
export * from './object';
export * from './string';

View File

@ -0,0 +1,3 @@
export const isNull = < T >( term: T | null ): term is null => {
return term === null;
};

View File

@ -0,0 +1,3 @@
export const isNumber = < U >( term: number | U ): term is number => {
return typeof term === 'number';
};

View File

@ -0,0 +1,23 @@
/**
* Internal dependencies
*/
import { isNull } from './null';
export const isObject = < T extends Record< string, unknown >, U >(
term: T | U
): term is NonNullable< T > => {
return (
! isNull( term ) &&
term instanceof Object &&
term.constructor === Object
);
};
export function objectHasProp< P extends PropertyKey >(
target: unknown,
property: P
): target is { [ K in P ]: unknown } {
// The `in` operator throws a `TypeError` for non-object values.
return isObject( target ) && property in target;
}

View File

@ -0,0 +1,3 @@
export const isString = < U >( term: string | U ): term is string => {
return typeof term === 'string';
};

View File

@ -118,10 +118,18 @@ class MiniCart extends AbstractBlock {
''
);
$cart_payload = $this->get_cart_payload();
$this->asset_data_registry->add(
'displayCartPricesIncludingTax',
$this->display_cart_prices_including_tax,
false
'cartTotals',
isset( $cart_payload['totals'] ) ? $cart_payload['totals'] : null,
null
);
$this->asset_data_registry->add(
'cartItemsCount',
isset( $cart_payload['items_count'] ) ? $cart_payload['items_count'] : null,
null
);
}
@ -447,6 +455,17 @@ class MiniCart extends AbstractBlock {
);
}
/**
* Get Cart Payload.
*
* @return object;
*/
protected function get_cart_payload() {
return WC()->api->get_endpoint_data( '/wc/store/cart' );
}
/**
* Get the supports array for this block type.
*