Display the link to add the shipping address when shipping address is not available (https://github.com/woocommerce/woocommerce-blocks/pull/8141)

This commit is contained in:
Tarun Vijwani 2023-03-22 11:15:13 +04:00 committed by GitHub
parent 6a629c60b2
commit 82c59ffb29
13 changed files with 570 additions and 98 deletions

View File

@ -2,56 +2,15 @@
* External dependencies * External dependencies
*/ */
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { ShippingAddress, getSetting } from '@woocommerce/settings';
import { decodeEntities } from '@wordpress/html-entities';
interface ShippingLocationProps { interface ShippingLocationProps {
address: ShippingAddress; formattedLocation: string | null;
} }
/** // Shows a formatted shipping location.
* Shows a formatted shipping location.
*
* @param {Object} props Incoming props for the component.
* @param {Object} props.address Incoming address information.
*/
const ShippingLocation = ( { const ShippingLocation = ( {
address, formattedLocation,
}: ShippingLocationProps ): JSX.Element | null => { }: ShippingLocationProps ): JSX.Element | null => {
// we bail early if we don't have an address.
if ( Object.values( address ).length === 0 ) {
return null;
}
const shippingCountries = getSetting( 'shippingCountries', {} ) as Record<
string,
string
>;
const shippingStates = getSetting( 'shippingStates', {} ) as Record<
string,
Record< string, string >
>;
const formattedCountry =
typeof shippingCountries[ address.country ] === 'string'
? decodeEntities( shippingCountries[ address.country ] )
: '';
const formattedState =
typeof shippingStates[ address.country ] === 'object' &&
typeof shippingStates[ address.country ][ address.state ] === 'string'
? decodeEntities(
shippingStates[ address.country ][ address.state ]
)
: address.state;
const addressParts = [];
addressParts.push( address.postcode.toUpperCase() );
addressParts.push( address.city );
addressParts.push( formattedState );
addressParts.push( formattedCountry );
const formattedLocation = addressParts.filter( Boolean ).join( ', ' );
if ( ! formattedLocation ) { if ( ! formattedLocation ) {
return null; return null;
} }

View File

@ -15,15 +15,20 @@ export const CalculatorButton = ( {
setIsShippingCalculatorOpen, setIsShippingCalculatorOpen,
}: CalculatorButtonProps ): JSX.Element => { }: CalculatorButtonProps ): JSX.Element => {
return ( return (
<button <a
className="wc-block-components-totals-shipping__change-address-button" role="button"
onClick={ () => { href="#wc-block-components-shipping-calculator-address__link"
className="wc-block-components-totals-shipping__change-address__link"
id="wc-block-components-totals-shipping__change-address__link"
onClick={ ( e ) => {
e.preventDefault();
setIsShippingCalculatorOpen( ! isShippingCalculatorOpen ); setIsShippingCalculatorOpen( ! isShippingCalculatorOpen );
} } } }
aria-label={ label }
aria-expanded={ isShippingCalculatorOpen } aria-expanded={ isShippingCalculatorOpen }
> >
{ label } { label }
</button> </a>
); );
}; };

View File

@ -10,6 +10,7 @@ import type { Currency } from '@woocommerce/price-format';
import { ShippingVia } from '@woocommerce/base-components/cart-checkout/totals/shipping/shipping-via'; import { ShippingVia } from '@woocommerce/base-components/cart-checkout/totals/shipping/shipping-via';
import { useSelect } from '@wordpress/data'; import { useSelect } from '@wordpress/data';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data'; import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import { isAddressComplete } from '@woocommerce/base-utils';
/** /**
* Internal dependencies * Internal dependencies
@ -67,6 +68,8 @@ export const TotalsShipping = ( {
} }
); );
const addressComplete = isAddressComplete( shippingAddress );
return ( return (
<div <div
className={ classnames( className={ classnames(
@ -77,23 +80,26 @@ export const TotalsShipping = ( {
<TotalsItem <TotalsItem
label={ __( 'Shipping', 'woo-gutenberg-products-block' ) } label={ __( 'Shipping', 'woo-gutenberg-products-block' ) }
value={ value={
hasRates && cartHasCalculatedShipping ? ( hasRates && cartHasCalculatedShipping
totalShippingValue ? totalShippingValue
) : ( : // if address is not complete, display the link to add an address.
<ShippingPlaceholder ! addressComplete && (
showCalculator={ showCalculator } <ShippingPlaceholder
isCheckout={ isCheckout } showCalculator={ showCalculator }
isShippingCalculatorOpen={ isCheckout={ isCheckout }
isShippingCalculatorOpen isShippingCalculatorOpen={
} isShippingCalculatorOpen
setIsShippingCalculatorOpen={ }
setIsShippingCalculatorOpen setIsShippingCalculatorOpen={
} setIsShippingCalculatorOpen
/> }
) />
)
} }
description={ description={
hasRates && cartHasCalculatedShipping ? ( // If address is complete, display the shipping address.
( hasRates && cartHasCalculatedShipping ) ||
addressComplete ? (
<> <>
<ShippingVia <ShippingVia
selectedShippingRates={ selectedShippingRates } selectedShippingRates={ selectedShippingRates }
@ -132,6 +138,7 @@ export const TotalsShipping = ( {
hasRates={ hasRates } hasRates={ hasRates }
shippingRates={ shippingRates } shippingRates={ shippingRates }
isLoadingRates={ isLoadingRates } isLoadingRates={ isLoadingRates }
isAddressComplete={ addressComplete }
/> />
) } ) }
</div> </div>

View File

@ -3,6 +3,11 @@
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { EnteredAddress } from '@woocommerce/settings'; import { EnteredAddress } from '@woocommerce/settings';
import {
formatShippingAddress,
isAddressComplete,
} from '@woocommerce/base-utils';
import { useEditorContext } from '@woocommerce/base-context';
/** /**
* Internal dependencies * Internal dependencies
@ -23,13 +28,21 @@ export const ShippingAddress = ( {
setIsShippingCalculatorOpen, setIsShippingCalculatorOpen,
shippingAddress, shippingAddress,
}: ShippingAddressProps ): JSX.Element | null => { }: ShippingAddressProps ): JSX.Element | null => {
const addressComplete = isAddressComplete( shippingAddress );
const { isEditor } = useEditorContext();
// If the address is incomplete, and we're not in the editor, don't show anything.
if ( ! addressComplete && ! isEditor ) {
return null;
}
const formattedLocation = formatShippingAddress( shippingAddress );
return ( return (
<> <>
<ShippingLocation address={ shippingAddress } /> <ShippingLocation formattedLocation={ formattedLocation } />
{ showCalculator && ( { showCalculator && (
<CalculatorButton <CalculatorButton
label={ __( label={ __(
'(change address)', 'Change address',
'woo-gutenberg-products-block' 'woo-gutenberg-products-block'
) } ) }
isShippingCalculatorOpen={ isShippingCalculatorOpen } isShippingCalculatorOpen={ isShippingCalculatorOpen }

View File

@ -39,6 +39,10 @@ export const ShippingPlaceholder = ( {
return ( return (
<CalculatorButton <CalculatorButton
label={ __(
'Add an address for shipping options',
'woo-gutenberg-products-block'
) }
isShippingCalculatorOpen={ isShippingCalculatorOpen } isShippingCalculatorOpen={ isShippingCalculatorOpen }
setIsShippingCalculatorOpen={ setIsShippingCalculatorOpen } setIsShippingCalculatorOpen={ setIsShippingCalculatorOpen }
/> />

View File

@ -2,8 +2,6 @@
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { Notice } from 'wordpress-components';
import classnames from 'classnames';
import type { CartResponseShippingRate } from '@woocommerce/types'; import type { CartResponseShippingRate } from '@woocommerce/types';
/** /**
@ -15,12 +13,14 @@ export interface ShippingRateSelectorProps {
hasRates: boolean; hasRates: boolean;
shippingRates: CartResponseShippingRate[]; shippingRates: CartResponseShippingRate[];
isLoadingRates: boolean; isLoadingRates: boolean;
isAddressComplete: boolean;
} }
export const ShippingRateSelector = ( { export const ShippingRateSelector = ( {
hasRates, hasRates,
shippingRates, shippingRates,
isLoadingRates, isLoadingRates,
isAddressComplete,
}: ShippingRateSelectorProps ): JSX.Element => { }: ShippingRateSelectorProps ): JSX.Element => {
const legend = hasRates const legend = hasRates
? __( 'Shipping options', 'woo-gutenberg-products-block' ) ? __( 'Shipping options', 'woo-gutenberg-products-block' )
@ -31,18 +31,13 @@ export const ShippingRateSelector = ( {
<ShippingRatesControl <ShippingRatesControl
className="wc-block-components-totals-shipping__options" className="wc-block-components-totals-shipping__options"
noResultsMessage={ noResultsMessage={
<Notice <>
isDismissible={ false } { isAddressComplete &&
className={ classnames( __(
'wc-block-components-shipping-rates-control__no-results-notice', 'There are no shipping options available. Please check your shipping address.',
'woocommerce-error' 'woo-gutenberg-products-block'
) } ) }
> </>
{ __(
'No shipping options were found.',
'woo-gutenberg-products-block'
) }
</Notice>
} }
shippingRates={ shippingRates } shippingRates={ shippingRates }
isLoadingRates={ isLoadingRates } isLoadingRates={ isLoadingRates }

View File

@ -20,12 +20,16 @@
flex-basis: 100%; flex-basis: 100%;
text-align: left; text-align: left;
} }
margin-top: ($gap-small);
} }
.wc-block-components-shipping-rates-control__no-results-notice { .wc-block-components-shipping-rates-control__no-results-notice {
margin: 0 0 em($gap-small); margin: 0 0 em($gap-small);
} }
.wc-block-components-totals-shipping__change-address__link {
font-weight: normal;
}
.wc-block-components-totals-shipping__change-address-button { .wc-block-components-totals-shipping__change-address-button {
@include link-button(); @include link-button();

View File

@ -0,0 +1,282 @@
/**
* External dependencies
*/
import { screen, render } from '@testing-library/react';
import { SlotFillProvider } from '@woocommerce/blocks-checkout';
import { previewCart as mockPreviewCart } from '@woocommerce/resource-previews';
import * as wpData from '@wordpress/data';
import * as baseContextHooks from '@woocommerce/base-context/hooks';
/**
* Internal dependencies
*/
import { TotalsShipping } from '../index';
jest.mock( '@wordpress/data', () => ( {
__esModule: true,
...jest.requireActual( '@wordpress/data' ),
useSelect: jest.fn(),
} ) );
wpData.useSelect.mockImplementation( () => {
return { prefersCollection: false };
} );
const shippingAddress = {
first_name: 'John',
last_name: 'Doe',
company: 'Company',
address_1: '409 Main Street',
address_2: 'Apt 1',
city: 'London',
postcode: 'W1T 4JG',
country: 'GB',
state: '',
email: 'john.doe@company',
phone: '+1234567890',
};
jest.mock( '@woocommerce/base-context/hooks', () => {
return {
__esModule: true,
...jest.requireActual( '@woocommerce/base-context/hooks' ),
useShippingData: jest.fn(),
useStoreCart: jest.fn(),
};
} );
baseContextHooks.useShippingData.mockReturnValue( {
needsShipping: true,
shippingRates: [
{
package_id: 0,
name: 'Shipping method',
destination: {
address_1: '',
address_2: '',
city: '',
state: '',
postcode: '',
country: '',
},
items: [
{
key: 'fb0c0a746719a7596f296344b80cb2b6',
name: 'Hoodie - Blue, Yes',
quantity: 1,
},
{
key: '1f0e3dad99908345f7439f8ffabdffc4',
name: 'Beanie',
quantity: 1,
},
],
shipping_rates: [
{
rate_id: 'flat_rate:1',
name: 'Flat rate',
description: '',
delivery_time: '',
price: '500',
taxes: '0',
instance_id: 1,
method_id: 'flat_rate',
meta_data: [
{
key: 'Items',
value: 'Hoodie - Blue, Yes &times; 1, Beanie &times; 1',
},
],
selected: false,
currency_code: 'USD',
currency_symbol: '$',
currency_minor_unit: 2,
currency_decimal_separator: '.',
currency_thousand_separator: ',',
currency_prefix: '$',
currency_suffix: '',
},
{
rate_id: 'local_pickup:2',
name: 'Local pickup',
description: '',
delivery_time: '',
price: '0',
taxes: '0',
instance_id: 2,
method_id: 'local_pickup',
meta_data: [
{
key: 'Items',
value: 'Hoodie - Blue, Yes &times; 1, Beanie &times; 1',
},
],
selected: false,
currency_code: 'USD',
currency_symbol: '$',
currency_minor_unit: 2,
currency_decimal_separator: '.',
currency_thousand_separator: ',',
currency_prefix: '$',
currency_suffix: '',
},
{
rate_id: 'free_shipping:5',
name: 'Free shipping',
description: '',
delivery_time: '',
price: '0',
taxes: '0',
instance_id: 5,
method_id: 'free_shipping',
meta_data: [
{
key: 'Items',
value: 'Hoodie - Blue, Yes &times; 1, Beanie &times; 1',
},
],
selected: true,
currency_code: 'USD',
currency_symbol: '$',
currency_minor_unit: 2,
currency_decimal_separator: '.',
currency_thousand_separator: ',',
currency_prefix: '$',
currency_suffix: '',
},
],
},
],
} );
baseContextHooks.useStoreCart.mockReturnValue( {
cartItems: mockPreviewCart.items,
cartTotals: [ mockPreviewCart.totals ],
cartCoupons: mockPreviewCart.coupons,
cartFees: mockPreviewCart.fees,
cartNeedsShipping: mockPreviewCart.needs_shipping,
shippingRates: [],
shippingAddress,
billingAddress: mockPreviewCart.billing_address,
cartHasCalculatedShipping: mockPreviewCart.has_calculated_shipping,
isLoadingRates: false,
} );
describe( 'TotalsShipping', () => {
it( 'should show correct calculator button label if address is complete', () => {
render(
<SlotFillProvider>
<TotalsShipping
currency={ {
code: 'USD',
symbol: '$',
position: 'left',
precision: 2,
} }
values={ {
total_shipping: '0',
total_shipping_tax: '0',
} }
showCalculator={ true }
showRateSelector={ true }
isCheckout={ true }
className={ '' }
/>
</SlotFillProvider>
);
expect(
screen.getByText(
'Shipping to W1T 4JG, London, United Kingdom (UK)'
)
).toBeInTheDocument();
expect( screen.getByText( 'Change address' ) ).toBeInTheDocument();
} );
it( 'should show correct calculator button label if address is incomplete', () => {
baseContextHooks.useStoreCart.mockReturnValue( {
cartItems: mockPreviewCart.items,
cartTotals: [ mockPreviewCart.totals ],
cartCoupons: mockPreviewCart.coupons,
cartFees: mockPreviewCart.fees,
cartNeedsShipping: mockPreviewCart.needs_shipping,
shippingRates: [],
shippingAddress: {
...shippingAddress,
city: '',
country: '',
postcode: '',
},
billingAddress: mockPreviewCart.billing_address,
cartHasCalculatedShipping: mockPreviewCart.has_calculated_shipping,
isLoadingRates: false,
} );
render(
<SlotFillProvider>
<TotalsShipping
currency={ {
code: 'USD',
symbol: '$',
position: 'left',
precision: 2,
} }
values={ {
total_shipping: '0',
total_shipping_tax: '0',
} }
showCalculator={ true }
showRateSelector={ true }
isCheckout={ true }
className={ '' }
/>
</SlotFillProvider>
);
expect(
screen.queryByText( 'Change address' )
).not.toBeInTheDocument();
expect(
screen.getByText( 'Add an address for shipping options' )
).toBeInTheDocument();
} );
it( 'does not show the calculator button when default rates are available and no address has been entered', () => {
baseContextHooks.useStoreCart.mockReturnValue( {
cartItems: mockPreviewCart.items,
cartTotals: [ mockPreviewCart.totals ],
cartCoupons: mockPreviewCart.coupons,
cartFees: mockPreviewCart.fees,
cartNeedsShipping: mockPreviewCart.needs_shipping,
shippingRates: mockPreviewCart.shipping_rates,
shippingAddress: {
...shippingAddress,
city: '',
country: '',
postcode: '',
},
billingAddress: mockPreviewCart.billing_address,
cartHasCalculatedShipping: mockPreviewCart.has_calculated_shipping,
isLoadingRates: false,
} );
render(
<SlotFillProvider>
<TotalsShipping
currency={ {
code: 'USD',
symbol: '$',
position: 'left',
precision: 2,
} }
values={ {
total_shipping: '0',
total_shipping_tax: '0',
} }
showCalculator={ true }
showRateSelector={ true }
isCheckout={ true }
className={ '' }
/>
</SlotFillProvider>
);
expect(
screen.queryByText( 'Change address' )
).not.toBeInTheDocument();
expect(
screen.queryByText( 'Add an address for shipping options' )
).not.toBeInTheDocument();
} );
} );

View File

@ -0,0 +1,36 @@
/**
* External dependencies
*/
import { screen, render } from '@testing-library/react';
/**
* Internal dependencies
*/
import ShippingPlaceholder from '../shipping-placeholder';
describe( 'ShippingPlaceholder', () => {
it( 'should show correct text if showCalculator is false', () => {
const { rerender } = render(
<ShippingPlaceholder
showCalculator={ false }
isCheckout={ true }
isShippingCalculatorOpen={ false }
setIsShippingCalculatorOpen={ jest.fn() }
/>
);
expect(
screen.getByText( 'No shipping options available' )
).toBeInTheDocument();
rerender(
<ShippingPlaceholder
showCalculator={ false }
isCheckout={ false }
isShippingCalculatorOpen={ false }
setIsShippingCalculatorOpen={ jest.fn() }
/>
);
expect(
screen.getByText( 'Calculated during checkout' )
).toBeInTheDocument();
} );
} );

View File

@ -11,7 +11,9 @@ import {
defaultAddressFields, defaultAddressFields,
ShippingAddress, ShippingAddress,
BillingAddress, BillingAddress,
getSetting,
} from '@woocommerce/settings'; } from '@woocommerce/settings';
import { decodeEntities } from '@wordpress/html-entities';
/** /**
* Compare two addresses and see if they are the same. * Compare two addresses and see if they are the same.
@ -100,3 +102,62 @@ export const emptyHiddenAddressFields = <
return newAddress; return newAddress;
}; };
/*
* Formats a shipping address for display.
*
* @param {Object} address The address to format.
* @return {string | null} The formatted address or null if no address is provided.
*/
export const formatShippingAddress = (
address: ShippingAddress | BillingAddress
): string | null => {
// We bail early if we don't have an address.
if ( Object.values( address ).length === 0 ) {
return null;
}
const shippingCountries = getSetting< Record< string, string > >(
'shippingCountries',
{}
);
const shippingStates = getSetting< Record< string, string > >(
'shippingStates',
{}
);
const formattedCountry =
typeof shippingCountries[ address.country ] === 'string'
? decodeEntities( shippingCountries[ address.country ] )
: '';
const formattedState =
typeof shippingStates[ address.country ] === 'object' &&
typeof shippingStates[ address.country ][ address.state ] === 'string'
? decodeEntities(
shippingStates[ address.country ][ address.state ]
)
: address.state;
const addressParts = [];
addressParts.push( address.postcode.toUpperCase() );
addressParts.push( address.city );
addressParts.push( formattedState );
addressParts.push( formattedCountry );
const formattedLocation = addressParts.filter( Boolean ).join( ', ' );
if ( ! formattedLocation ) {
return null;
}
return formattedLocation;
};
/**
* Returns true if the address has a city and country.
*/
export const isAddressComplete = (
address: ShippingAddress | BillingAddress
): boolean => {
return !! address.city && !! address.country;
};

View File

@ -1,7 +1,11 @@
/** /**
* External dependencies * External dependencies
*/ */
import { emptyHiddenAddressFields } from '@woocommerce/base-utils'; import {
emptyHiddenAddressFields,
isAddressComplete,
formatShippingAddress,
} from '@woocommerce/base-utils';
describe( 'emptyHiddenAddressFields', () => { describe( 'emptyHiddenAddressFields', () => {
it( "Removes state from an address where the country doesn't use states", () => { it( "Removes state from an address where the country doesn't use states", () => {
@ -22,3 +26,101 @@ describe( 'emptyHiddenAddressFields', () => {
expect( filteredAddress ).toHaveProperty( 'state', '' ); expect( filteredAddress ).toHaveProperty( 'state', '' );
} ); } );
} ); } );
describe( 'isAddressComplete', () => {
it( 'correctly checks empty addresses', () => {
const address = {
first_name: '',
last_name: '',
company: '',
address_1: '',
address_2: '',
city: '',
postcode: '',
country: '',
state: '',
email: '',
phone: '',
};
expect( isAddressComplete( address ) ).toBe( false );
} );
it( 'correctly checks incomplete addresses', () => {
const address = {
first_name: 'John',
last_name: 'Doe',
company: 'Company',
address_1: '409 Main Street',
address_2: 'Apt 1',
city: '',
postcode: '',
country: '',
state: '',
email: 'john.doe@company',
phone: '+1234567890',
};
expect( isAddressComplete( address ) ).toBe( false );
address.city = 'London';
expect( isAddressComplete( address ) ).toBe( false );
address.postcode = 'W1T 4JG';
address.country = 'GB';
expect( isAddressComplete( address ) ).toBe( true );
} );
it( 'correctly checks complete addresses', () => {
const address = {
first_name: 'John',
last_name: 'Doe',
company: 'Company',
address_1: '409 Main Street',
address_2: 'Apt 1',
city: 'London',
postcode: 'W1T 4JG',
country: 'GB',
state: '',
email: 'john.doe@company',
phone: '+1234567890',
};
expect( isAddressComplete( address ) ).toBe( true );
} );
} );
describe( 'formatShippingAddress', () => {
it( 'returns null if address is empty', () => {
const address = {
first_name: '',
last_name: '',
company: '',
address_1: '',
address_2: '',
city: '',
postcode: '',
country: '',
state: '',
email: '',
phone: '',
};
expect( formatShippingAddress( address ) ).toBe( null );
} );
it( 'correctly returns the formatted address', () => {
const address = {
first_name: 'John',
last_name: 'Doe',
company: 'Company',
address_1: '409 Main Street',
address_2: 'Apt 1',
city: 'London',
postcode: 'W1T 4JG',
country: 'GB',
state: '',
email: 'john.doe@company',
phone: '+1234567890',
};
expect( formatShippingAddress( address ) ).toBe(
'W1T 4JG, London, United Kingdom (UK)'
);
} );
} );

View File

@ -26,6 +26,7 @@ const Block = ( {
showRateSelector={ false } showRateSelector={ false }
values={ cartTotals } values={ cartTotals }
currency={ totalsCurrency } currency={ totalsCurrency }
isCheckout={ true }
/> />
</TotalsWrapper> </TotalsWrapper>
); );

View File

@ -7,22 +7,21 @@ import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout
import { import {
getShippingRatesPackageCount, getShippingRatesPackageCount,
hasCollectableRate, hasCollectableRate,
isAddressComplete,
} from '@woocommerce/base-utils'; } from '@woocommerce/base-utils';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount'; import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
import { useEditorContext, noticeContexts } from '@woocommerce/base-context'; import { useEditorContext, noticeContexts } from '@woocommerce/base-context';
import { StoreNoticesContainer } from '@woocommerce/blocks-checkout'; import { StoreNoticesContainer } from '@woocommerce/blocks-checkout';
import { decodeEntities } from '@wordpress/html-entities'; import { decodeEntities } from '@wordpress/html-entities';
import { Notice } from 'wordpress-components';
import classnames from 'classnames';
import { getSetting } from '@woocommerce/settings'; import { getSetting } from '@woocommerce/settings';
import type { import type {
PackageRateOption, PackageRateOption,
CartShippingPackageShippingRate, CartShippingPackageShippingRate,
} from '@woocommerce/types'; } from '@woocommerce/types';
import type { ReactElement } from 'react';
import { useSelect } from '@wordpress/data';
import { CART_STORE_KEY } from '@woocommerce/block-data'; import { CART_STORE_KEY } from '@woocommerce/block-data';
import { useSelect } from '@wordpress/data';
import type { ReactElement } from 'react';
/** /**
* Internal dependencies * Internal dependencies
@ -87,11 +86,16 @@ const Block = ( {
} ) } )
: shippingRates; : shippingRates;
const shippingAddress = useSelect( ( select ) => {
return select( CART_STORE_KEY ).getCustomerData()?.shippingAddress;
} );
if ( ! needsShipping ) { if ( ! needsShipping ) {
return null; return null;
} }
const shippingAddressIsComplete = ! shippingAddressHasValidationErrors(); const shippingAddressHasErrors = ! shippingAddressHasValidationErrors();
const addressComplete = isAddressComplete( shippingAddress );
const shippingRatesPackageCount = const shippingRatesPackageCount =
getShippingRatesPackageCount( shippingRates ); getShippingRatesPackageCount( shippingRates );
@ -99,7 +103,7 @@ const Block = ( {
if ( if (
( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) ||
( shippingCostRequiresAddress && ( shippingCostRequiresAddress &&
( ! shippingAddressPushed || ! shippingAddressIsComplete ) ) ( ! shippingAddressPushed || ! shippingAddressHasErrors ) )
) { ) {
return ( return (
<p> <p>
@ -121,18 +125,17 @@ const Block = ( {
) : ( ) : (
<ShippingRatesControl <ShippingRatesControl
noResultsMessage={ noResultsMessage={
<Notice <>
isDismissible={ false } { addressComplete
className={ classnames( ? __(
'wc-block-components-shipping-rates-control__no-results-notice', 'There are no shipping options available. Please check your shipping address.',
'woocommerce-error' 'woo-gutenberg-products-block'
) } )
> : __(
{ __( 'Add a shipping address to view shipping options.',
'There are no shipping options available. Please check your shipping address.', 'woo-gutenberg-products-block'
'woo-gutenberg-products-block' ) }
) } </>
</Notice>
} }
renderOption={ renderShippingRatesControlOption } renderOption={ renderShippingRatesControlOption }
collapsible={ false } collapsible={ false }