Commit 74e823c032 for woocommerce

commit 74e823c0324289cff7361629ff8cc677c45dce0f
Author: Mike Jolley <mike.jolley@me.com>
Date:   Thu May 29 16:33:40 2025 +0100

    Remove the shipping calculation section in the cart block (#58066)

    * Remove shipping calculator

    * Set context when loading cart for hydration

    * Remove special pickup handling in API and shipping controller

    * Rename coupon button

    * Update types

    * Remove more shipping UI from cart

    * Estimated total labels

    * Use shipping rate as row label

    * Exclude duplicate rate names in getSelectedShippingRateNames

    * Update styling in cart

    * Hide empty total rows in cart summary

    * Remove all shipping UI in cart

    * Changelog

    * Always show "Shipping will be calculated at checkout" when estimating totals

    * Unused import in schema

    * hasSelectedShippingRate helper

    * default to no selection for block checkout in wc_get_default_shipping_method_for_package

    * show_shipping true when using local pickup in block context

    * Update settings

    * Rename prop for clarity

    * Fix CSS formatting

    * Remove shipping address test

    * Fix type export

    * fix missing var

    * Reduce size of diff

    * Update snapshots due to wording change

    * Remove shipping calc tests

    * Use desc_tip for setting

    * update helper for pickup tests

    * update mock

    * Remove duplicate tests

    * Allow default to local pickup if there are no other methods

    * Remove cart tests

    * Use inferred types

    * Inline comment to explain what is happening with the array

    * isEstimate

    * Add comment for the empty selector

    * Remove shipping CSS from cart block styles

    * Remove shippingBlock selector from tests

    * Remove shipping calc utilities from e2e tests

    * Put `includes` string back to `including`

    * remove references to shipping calc in docs

    * Hide `Shipping will be calculated at checkout` if rate is selected already

    * Fix white space in snapshot test

    * Update docs manifest

    * Hide shipping notice if cart does not require shipping

    * return early if ! cartNeedsShipping

    * replace find with some in hasSelectedShippingRate

    * Prevent double border if totals wrapper is empty

    * Update plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/index.tsx

    Co-authored-by: Łukasz Strączyński <8038404+straku@users.noreply.github.com>

    * Remove parentheses again

    ---------

    Co-authored-by: Łukasz Strączyński <8038404+straku@users.noreply.github.com>

diff --git a/docs/cart-and-checkout-blocks/overview-of-data-flow.md b/docs/cart-and-checkout-blocks/overview-of-data-flow.md
index e2e3de2718..fb639df9ac 100644
--- a/docs/cart-and-checkout-blocks/overview-of-data-flow.md
+++ b/docs/cart-and-checkout-blocks/overview-of-data-flow.md
@@ -128,7 +128,3 @@ The items listed in the Checkout section above also apply to the Cart block (bes
 ### Changing item quantity, adding, or removing items

 When the shopper updates an item's quantity, removes an item from their cart, or adds an item (e.g. from the Cart cross-sells block) a Store API request is made. The local cart is updated with the response.
-
-### Using the shipping calculator
-
-This behaves the same way as the address forms in the Checkout block, however the address in the shipping calculator is only sent to the server if the postcode is valid, and all required fields have values.
diff --git a/docs/docs-manifest.json b/docs/docs-manifest.json
index 6a31dc1537..c99cdbae32 100644
--- a/docs/docs-manifest.json
+++ b/docs/docs-manifest.json
@@ -129,7 +129,7 @@
           "menu_title": "Data flow overview",
           "tags": "reference",
           "edit_url": "https://github.com/woocommerce/woocommerce/edit/trunk/docs/cart-and-checkout-blocks/overview-of-data-flow.md",
-          "hash": "26acb354552d747ae8249547c8df7d640cf719a6c6c3e4eccbc97fce29c87bad",
+          "hash": "5f4166eb07de6b9cea6a7100dcd8cb4173f9eea44f2a53db28755cecadd80eaf",
           "url": "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/docs/cart-and-checkout-blocks/overview-of-data-flow.md",
           "id": "d2d8cf26967438f16815b129da855324be7a7ef8",
           "links": {
@@ -2000,5 +2000,5 @@
       "categories": []
     }
   ],
-  "hash": "2ece70c4199ed2d1c83128b150a4ab77d83bb5c25afc454630e6e6b47df43cc5"
+  "hash": "438f603d1c0fe71a61300aab65b432e37a1b620ac4464318f6592c8c14c08f9d"
 }
\ No newline at end of file
diff --git a/plugins/woocommerce/changelog/wooplug-4278-remove-the-shipping-calculation-section-in-the-cart-block b/plugins/woocommerce/changelog/wooplug-4278-remove-the-shipping-calculation-section-in-the-cart-block
new file mode 100644
index 0000000000..c4a760c7f5
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-4278-remove-the-shipping-calculation-section-in-the-cart-block
@@ -0,0 +1,4 @@
+Significance: major
+Type: enhancement
+
+Streamlined the Cart Block to include only estimated totals and remove complex/confusing shipping selection and calculation UI. Shipping is selected during checkout.
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/form/use-form-fields.ts b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/form/use-form-fields.ts
index 13ebc5b7c1..654f3461ba 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/form/use-form-fields.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/form/use-form-fields.ts
@@ -25,7 +25,7 @@ export const useFormFields = < T extends keyof FormFields >(
 	fieldKeys: T[],
 	// Default fields from settings.
 	defaultFields: FormFields,
-	// Form type, can be billing, shipping, contact, order, or calculator.
+	// Form type, can be billing, shipping, contact, order.
 	formType: FormType,
 	// Address country.
 	addressCountry = ''
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/form/use-form-validation.ts b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/form/use-form-validation.ts
index 3b282e39bb..1b6902f2c5 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/form/use-form-validation.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/form/use-form-validation.ts
@@ -94,7 +94,7 @@ const EMPTY_OBJECT: FormErrors = {};
  */
 export const useFormValidation = (
 	formFields: KeyedFormFields,
-	// Form type, can be billing, shipping, contact, order, or calculator.
+	// Form type, can be billing, shipping, contact, order.
 	formType: FormType,
 	// Allows certain fields to be overridden for forms that don't auto update data store.
 	overrideValues?: AddressFormValues
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/index.js b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/index.js
index 01d6548582..6b03afc288 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/index.js
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/index.js
@@ -18,4 +18,3 @@ export { default as PaymentMethodLabel } from './payment-method-label';
 export { default as AdditionalFieldsPlaceholder } from './additional-fields-placeholder';
 export { default as PasswordStrengthMeter } from './password-strength-meter';
 export * from './totals';
-export * from './shipping-calculator';
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/address.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/address.tsx
deleted file mode 100644
index 1e9de8afb8..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/address.tsx
+++ /dev/null
@@ -1,138 +0,0 @@
-/**
- * External dependencies
- */
-import { __ } from '@wordpress/i18n';
-import Button from '@woocommerce/base-components/button';
-import { useState, useCallback } from '@wordpress/element';
-import isShallowEqual from '@wordpress/is-shallow-equal';
-import type { ShippingAddress, AddressForm } from '@woocommerce/settings';
-import { validationStore, cartStore } from '@woocommerce/block-data';
-import { useDispatch, useSelect } from '@wordpress/data';
-import { useFocusReturn } from '@woocommerce/base-utils';
-import { useCheckoutAddress } from '@woocommerce/base-context';
-
-/**
- * Internal dependencies
- */
-import './style.scss';
-import { Form } from '../form';
-import { useFormFields } from '../form/use-form-fields';
-
-interface ShippingCalculatorAddressProps {
-	address: ShippingAddress;
-	onUpdate: ( address: Partial< ShippingAddress > ) => void;
-	onCancel: () => void;
-	addressFields: Partial< keyof AddressForm >[];
-}
-const ShippingCalculatorAddress = ( {
-	address: initialAddress,
-	onUpdate,
-	onCancel,
-	addressFields,
-}: ShippingCalculatorAddressProps ): JSX.Element => {
-	const [ address, setAddress ] = useState( initialAddress );
-	const { showAllValidationErrors } = useDispatch( validationStore );
-	const focusReturnRef = useFocusReturn();
-	const { hasValidationErrors, isAddressFieldsForShippingRatesUpdating } =
-		useSelect( ( select ) => {
-			return {
-				hasValidationErrors:
-					select( validationStore ).hasValidationErrors(),
-				isAddressFieldsForShippingRatesUpdating:
-					select(
-						cartStore
-					).isAddressFieldsForShippingRatesUpdating(),
-			};
-		}, [] );
-	const { defaultFields } = useCheckoutAddress();
-	const formFields = useFormFields(
-		addressFields,
-		defaultFields,
-		'shipping',
-		address.country
-	);
-
-	const hasRequiredFields = useCallback( () => {
-		for ( const field of formFields ) {
-			if ( field.required && ! field.hidden ) {
-				const value = address[ field.key ];
-
-				if ( typeof value === 'string' ) {
-					if ( value.trim() === '' ) {
-						return false;
-					}
-					continue;
-				}
-
-				// TODO: Handle boolean fields if needed
-				// Currently, any non-string value fails validation
-				return false;
-			}
-		}
-		return true;
-	}, [ formFields, address ] );
-
-	const handleClick = useCallback(
-		( e: React.MouseEvent< HTMLButtonElement > ) => {
-			e.preventDefault();
-
-			showAllValidationErrors();
-
-			const isAddressValid = ! hasValidationErrors && hasRequiredFields();
-
-			if ( isAddressValid ) {
-				const addressChanged = ! isShallowEqual(
-					address,
-					initialAddress
-				);
-
-				if ( ! addressChanged ) {
-					return onCancel();
-				}
-
-				const addressToSubmit: Partial< ShippingAddress > =
-					Object.fromEntries(
-						addressFields
-							.filter( ( key ) => address[ key ] !== undefined )
-							.map( ( key ) => [ key, address[ key ] ] )
-					);
-
-				onUpdate( addressToSubmit );
-			}
-		},
-		[
-			showAllValidationErrors,
-			hasValidationErrors,
-			hasRequiredFields,
-			address,
-			initialAddress,
-			addressFields,
-			onCancel,
-			onUpdate,
-		]
-	);
-
-	return (
-		<form
-			className="wc-block-components-shipping-calculator-address"
-			ref={ focusReturnRef }
-		>
-			<Form
-				fields={ addressFields }
-				onChange={ setAddress }
-				values={ address }
-			/>
-			<Button
-				className="wc-block-components-shipping-calculator-address__button"
-				disabled={ isAddressFieldsForShippingRatesUpdating }
-				variant="outlined"
-				onClick={ handleClick }
-				type="submit"
-			>
-				{ __( 'Check delivery options', 'woocommerce' ) }
-			</Button>
-		</form>
-	);
-};
-
-export default ShippingCalculatorAddress;
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/context.ts b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/context.ts
deleted file mode 100644
index 83e5952c57..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/context.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * External dependencies
- */
-import { createContext } from '@wordpress/element';
-
-type ShippingCalculatorContextType = {
-	shippingCalculatorID: string;
-	showCalculator: boolean;
-	isShippingCalculatorOpen: boolean;
-	setIsShippingCalculatorOpen: React.Dispatch<
-		React.SetStateAction< boolean >
-	>;
-};
-
-export const ShippingCalculatorContext =
-	createContext< ShippingCalculatorContextType >( {
-		shippingCalculatorID: '',
-		showCalculator: false,
-		isShippingCalculatorOpen: false,
-		setIsShippingCalculatorOpen: () => {
-			/* Do nothing */
-		},
-	} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx
deleted file mode 100644
index 9d7a82671e..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/index.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './shipping-calculator';
-export * from './shipping-calculator-panel';
-export * from './context';
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/shipping-calculator-panel.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/shipping-calculator-panel.tsx
deleted file mode 100644
index da305f6a40..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/shipping-calculator-panel.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * External dependencies
- */
-import { useContext } from '@wordpress/element';
-import { Panel } from '@woocommerce/blocks-components';
-
-/**
- * Internal dependencies
- */
-import { ShippingCalculatorContext } from './context';
-import './style.scss';
-import ShippingCalculator from './shipping-calculator';
-
-type ShippingCalculatorPanelProps = {
-	title: string | React.ReactNode;
-};
-
-export const ShippingCalculatorPanel = ( {
-	title,
-}: ShippingCalculatorPanelProps ) => {
-	const { isShippingCalculatorOpen, setIsShippingCalculatorOpen } =
-		useContext( ShippingCalculatorContext );
-	return (
-		<Panel
-			className="wc-block-components-totals-shipping-panel"
-			initialOpen={ false }
-			hasBorder={ false }
-			title={ title }
-			state={ [ isShippingCalculatorOpen, setIsShippingCalculatorOpen ] }
-		>
-			<ShippingCalculator />
-		</Panel>
-	);
-};
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/shipping-calculator.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/shipping-calculator.tsx
deleted file mode 100644
index ae3d5b52cf..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/shipping-calculator.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-/**
- * External dependencies
- */
-import { useContext, useCallback } from '@wordpress/element';
-import type { ShippingAddress } from '@woocommerce/settings';
-import { useCustomerData } from '@woocommerce/base-context/hooks';
-import { dispatch } from '@wordpress/data';
-import { cartStore, processErrorResponse } from '@woocommerce/block-data';
-import { StoreNoticesContainer } from '@woocommerce/blocks-components';
-import { removeNoticesWithContext } from '@woocommerce/base-utils';
-
-/**
- * Internal dependencies
- */
-import ShippingCalculatorAddress from './address';
-import { ShippingCalculatorContext } from './context';
-import './style.scss';
-
-interface ShippingCalculatorProps {
-	onUpdate?: ( newAddress: ShippingAddress ) => void;
-	onCancel?: () => void;
-	addressFields?: Partial< keyof ShippingAddress >[];
-}
-
-export const ShippingCalculator = ( {
-	onUpdate = () => {
-		/* Do nothing */
-	},
-	onCancel = () => {
-		/* Do nothing */
-	},
-	addressFields = [ 'country', 'state', 'city', 'postcode' ],
-}: ShippingCalculatorProps ): JSX.Element | null => {
-	const {
-		shippingCalculatorID,
-		showCalculator,
-		setIsShippingCalculatorOpen,
-	} = useContext( ShippingCalculatorContext );
-	const { shippingAddress } = useCustomerData();
-	const noticeContext = 'wc/cart/shipping-calculator';
-
-	const handleCancel = useCallback( () => {
-		setIsShippingCalculatorOpen( false );
-		onCancel();
-	}, [ setIsShippingCalculatorOpen, onCancel ] );
-
-	const handleUpdate = useCallback(
-		( newAddress: ShippingAddress ) => {
-			// Updates the address and waits for the result.
-			dispatch( cartStore )
-				.updateCustomerData(
-					{
-						shipping_address: newAddress,
-					},
-					false,
-					true // address fields for shipping rates changed
-				)
-				.then( () => {
-					removeNoticesWithContext( noticeContext );
-					setIsShippingCalculatorOpen( false );
-					onUpdate( newAddress );
-				} )
-				.catch( ( response ) => {
-					processErrorResponse( response, noticeContext );
-				} );
-		},
-		[ onUpdate, setIsShippingCalculatorOpen ]
-	);
-
-	if ( ! showCalculator ) {
-		return null;
-	}
-
-	return (
-		<div
-			className="wc-block-components-shipping-calculator"
-			id={ shippingCalculatorID }
-		>
-			<StoreNoticesContainer context={ noticeContext } />
-			<ShippingCalculatorAddress
-				address={ shippingAddress }
-				addressFields={ addressFields }
-				onCancel={ handleCancel }
-				onUpdate={ handleUpdate }
-			/>
-		</div>
-	);
-};
-
-export default ShippingCalculator;
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/style.scss b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/style.scss
deleted file mode 100644
index d8c78b3063..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/style.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-.wp-block-woocommerce-cart-order-summary-shipping-block {
-	.wc-block-components-totals-shipping-panel {
-		.wc-block-components-panel__button {
-			padding-top: 0;
-		}
-
-		.wc-block-components-shipping-calculator {
-			padding-top: 0;
-		}
-	}
-}
-
-.wc-block-components-shipping-calculator-address {
-	margin-bottom: 0;
-}
-
-.wc-block-components-totals-shipping__change-address__link {
-	white-space: nowrap;
-}
-
-.wc-block-components-shipping-calculator-address__button {
-	width: 100%;
-	margin-top: em($gap-large);
-}
-
-.wc-block-components-shipping-calculator {
-	padding: em($gap-smaller) 0 em($gap-small);
-}
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/test/index.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/test/index.tsx
deleted file mode 100644
index 865b9f95d2..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/shipping-calculator/test/index.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * External dependencies
- */
-import { fireEvent, render, screen } from '@testing-library/react';
-import '@testing-library/jest-dom';
-import type { ShippingAddress } from '@woocommerce/settings';
-
-/**
- * Internal dependencies
- */
-import ShippingCalculatorAddress from '../address';
-
-const address: ShippingAddress = {
-	first_name: '',
-	last_name: '',
-	company: '',
-	address_1: '',
-	address_2: '',
-	city: '',
-	state: '',
-	postcode: '',
-	country: '',
-	phone: '',
-};
-
-const addressFields: Array< 'country' | 'city' | 'state' | 'postcode' > = [
-	'country',
-	'city',
-	'state',
-	'postcode',
-];
-
-describe( 'ShippingCalculatorAddress', () => {
-	it( 'should show inline validation errors when the form is submitted with empty fields', () => {
-		render(
-			<ShippingCalculatorAddress
-				address={ address }
-				onUpdate={ jest.fn() }
-				onCancel={ jest.fn() }
-				addressFields={ addressFields }
-			/>
-		);
-
-		// Submit form with empty fields
-		fireEvent.click(
-			screen.getByRole( 'button', { name: 'Check delivery options' } )
-		);
-
-		// Verify validation errors are shown
-		const errorMessages = [
-			'Please enter a valid city',
-			'Please enter a valid state/county',
-			'Please enter a valid postal code',
-		];
-		errorMessages.forEach( ( message ) => {
-			expect( screen.getByText( message ) ).toBeInTheDocument();
-		} );
-
-		// Fill in all required fields
-		const fields = {
-			City: 'Vienna',
-			'State/County': 'Vienna',
-			'Postal code': '1010',
-		};
-		Object.entries( fields ).forEach( ( [ label, value ] ) => {
-			fireEvent.change( screen.getByRole( 'textbox', { name: label } ), {
-				target: { value },
-			} );
-		} );
-
-		// Submit form with filled fields
-		fireEvent.click(
-			screen.getByRole( 'button', { name: 'Check delivery options' } )
-		);
-
-		// Verify validation errors are cleared
-		errorMessages.forEach( ( message ) => {
-			expect( screen.queryByText( message ) ).not.toBeInTheDocument();
-		} );
-	} );
-} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/coupon/index.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/coupon/index.tsx
index 65e13ad1d3..bace3b68fb 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/coupon/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/coupon/index.tsx
@@ -87,7 +87,7 @@ export const TotalsCoupon = ( {
 			initialOpen={ isCouponFormVisible }
 			hasBorder={ false }
 			headingLevel={ 2 }
-			title={ __( 'Add a coupon', 'woocommerce' ) }
+			title={ __( 'Add coupons', 'woocommerce' ) }
 			state={ [ isCouponFormVisible, setIsCouponFormVisible ] }
 		>
 			<LoadingMask
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/coupon/test/index.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/coupon/test/index.tsx
index 14ffda2d96..78fea99be9 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/coupon/test/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/coupon/test/index.tsx
@@ -16,7 +16,7 @@ describe( 'TotalsCoupon', () => {
 		const user = userEvent.setup();
 		const { rerender } = render( <TotalsCoupon instanceId={ 'coupon' } /> );

-		const openCouponFormButton = screen.getByText( 'Add a coupon' );
+		const openCouponFormButton = screen.getByText( 'Add coupons' );
 		expect( openCouponFormButton ).toBeInTheDocument();
 		await act( async () => {
 			await user.click( openCouponFormButton );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.tsx
index b8a69e4eac..b110e17263 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/footer-item/index.tsx
@@ -20,6 +20,7 @@ import {
 	LooselyMustHave,
 } from '@woocommerce/types';
 import { formatPrice } from '@woocommerce/price-format';
+import { hasSelectedShippingRate } from '@woocommerce/base-utils';

 /**
  * Internal dependencies
@@ -27,10 +28,15 @@ import { formatPrice } from '@woocommerce/price-format';
 import './style.scss';

 export interface TotalsFooterItemProps {
+	className?: string;
 	/**
 	 * The currency object with which to display the item
 	 */
 	currency: Currency;
+	/**
+	 * Whether the totals are estimated e.g. in the cart.
+	 */
+	isEstimate?: boolean;
 	/**
 	 * An object containing the total price and the total tax
 	 *
@@ -38,7 +44,6 @@ export interface TotalsFooterItemProps {
 	 * convenience, but will use only these two properties.
 	 */
 	values: LooselyMustHave< CartResponseTotals, 'total_price' | 'total_tax' >;
-	className?: string;
 }

 /**
@@ -52,6 +57,7 @@ const TotalsFooterItem = ( {
 	currency,
 	values,
 	className,
+	isEstimate = false,
 }: TotalsFooterItemProps ): JSX.Element => {
 	const SHOW_TAXES =
 		getSetting< boolean >( 'taxesEnabled', true ) &&
@@ -70,7 +76,9 @@ const TotalsFooterItem = ( {

 	const label = applyCheckoutFilter( {
 		filterName: 'totalLabel',
-		defaultValue: __( 'Total', 'woocommerce' ),
+		defaultValue: isEstimate
+			? __( 'Estimated total', 'woocommerce' )
+			: __( 'Total', 'woocommerce' ),
 		extensions: cart.extensions,
 		arg: { cart },
 	} );
@@ -113,6 +121,9 @@ const TotalsFooterItem = ( {
 			  )
 			: __( 'Including <TaxAmount/> in taxes', 'woocommerce' );

+	const hasSelectedRates = hasSelectedShippingRate( cart.shippingRates );
+	const cartNeedsShipping = cart.cartNeedsShipping;
+
 	return (
 		<TotalsItem
 			className={ clsx(
@@ -123,20 +134,29 @@ const TotalsFooterItem = ( {
 			label={ label }
 			value={ value }
 			description={
-				SHOW_TAXES &&
-				parsedTaxValue !== 0 && (
-					<p className="wc-block-components-totals-footer-item-tax">
-						{ createInterpolateElement( description, {
-							TaxAmount: (
-								<FormattedMonetaryAmount
-									className="wc-block-components-totals-footer-item-tax-value"
-									currency={ currency }
-									value={ parsedTaxValue }
-								/>
-							),
-						} ) }
-					</p>
-				)
+				<>
+					{ SHOW_TAXES && parsedTaxValue !== 0 && (
+						<p className="wc-block-components-totals-footer-item-tax">
+							{ createInterpolateElement( description, {
+								TaxAmount: (
+									<FormattedMonetaryAmount
+										className="wc-block-components-totals-footer-item-tax-value"
+										currency={ currency }
+										value={ parsedTaxValue }
+									/>
+								),
+							} ) }
+						</p>
+					) }
+					{ isEstimate && ! hasSelectedRates && cartNeedsShipping && (
+						<p className="wc-block-components-totals-footer-item-shipping">
+							{ __(
+								'Shipping will be calculated at checkout',
+								'woocommerce'
+							) }
+						</p>
+					) }
+				</>
 			}
 		/>
 	);
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/footer-item/style.scss b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/footer-item/style.scss
index 08a9a35b4e..f4d138c5cb 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/footer-item/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/footer-item/style.scss
@@ -10,9 +10,16 @@

 	.wc-block-components-totals-footer-item-tax {
 		margin-bottom: 0;
+		margin-top: 0;
+		font-weight: 700;
+	}
+
+	.wc-block-components-totals-footer-item-shipping {
+		margin-bottom: 0;
+		margin-top: 0;
 	}

 	.wc-block-components-totals-item__value {
-		font-weight: bold;
+		font-weight: 700;
 	}
 }
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/index.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/index.tsx
index f1db6f3aa8..afa53a739a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/index.tsx
@@ -4,14 +4,16 @@
 import { __ } from '@wordpress/i18n';
 import { TotalsItem } from '@woocommerce/blocks-components';
 import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
-import { hasShippingRate } from '@woocommerce/base-utils';
+import {
+	hasSelectedShippingRate,
+	getSelectedShippingRateNames,
+} from '@woocommerce/base-utils';
 import { useStoreCart } from '@woocommerce/base-context';

 /**
  * Internal dependencies
  */
 import { ShippingVia } from './shipping-via';
-import { ShippingAddress } from './shipping-address';
 import { renderShippingTotalValue } from './utils';
 import './style.scss';

@@ -27,20 +29,24 @@ export const TotalsShipping = ( {
 	collaterals = null,
 }: TotalShippingProps ): JSX.Element | null => {
 	const { cartTotals, shippingRates } = useStoreCart();
-	const hasRates = hasShippingRate( shippingRates );
+	const hasSelectedRates = hasSelectedShippingRate( shippingRates );
+	const rateNames = getSelectedShippingRateNames( shippingRates );
+	const hasMultipleRates = rateNames.length > 1;
+	const rowLabel =
+		! hasSelectedRates || hasMultipleRates ? label : rateNames[ 0 ];
+
 	return (
 		<div className="wc-block-components-totals-shipping">
 			<TotalsItem
-				label={ label }
+				label={ rowLabel }
 				value={
-					hasRates
+					hasSelectedRates
 						? renderShippingTotalValue( cartTotals )
 						: placeholder
 				}
 				description={
 					<>
-						{ !! hasRates && <ShippingVia /> }
-						<ShippingAddress />
+						{ hasMultipleRates && <ShippingVia /> }
 						{ collaterals && (
 							<div className="wc-block-components-totals-shipping__collaterals">
 								{ collaterals }
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/shipping-address.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/shipping-address.tsx
deleted file mode 100644
index 7b5c7ff82b..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/shipping-address.tsx
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * External dependencies
- */
-import { __ } from '@wordpress/i18n';
-import {
-	formatShippingAddress,
-	hasShippingRate,
-	isAddressComplete,
-} from '@woocommerce/base-utils';
-import { useStoreCart } from '@woocommerce/base-context';
-import {
-	ShippingCalculatorPanel,
-	ShippingCalculatorContext,
-} from '@woocommerce/base-components/cart-checkout';
-import { useSelect } from '@wordpress/data';
-import { checkoutStore } from '@woocommerce/block-data';
-import { createInterpolateElement, useContext } from '@wordpress/element';
-import { getSetting } from '@woocommerce/settings';
-
-/**
- * Internal dependencies
- */
-import { getPickupLocation } from './utils';
-
-export const ShippingAddress = (): JSX.Element => {
-	const { shippingRates, shippingAddress } = useStoreCart();
-	const prefersCollection = useSelect( ( select ) =>
-		select( checkoutStore ).prefersCollection()
-	);
-
-	const hasRates = hasShippingRate( shippingRates );
-
-	const { showCalculator } = useContext( ShippingCalculatorContext );
-
-	const formattedAddress = prefersCollection
-		? getPickupLocation( shippingRates )
-		: formatShippingAddress( shippingAddress );
-
-	const deliversToLabel = hasRates
-		? // Translators: <address/> is the formatted shipping address.
-		  __( 'Delivers to <address/>', 'woocommerce' )
-		: // Translators: <address/> is the formatted shipping address.
-		  __( 'No delivery options available for <address/>', 'woocommerce' );
-
-	const addressComplete = isAddressComplete( shippingAddress, [
-		'state',
-		'city',
-		'country',
-		'postcode',
-	] );
-
-	const shippingCostRequiresAddress = getSetting< boolean >(
-		'shippingCostRequiresAddress',
-		false
-	);
-
-	const showEnterAddressMessage =
-		shippingCostRequiresAddress && ! addressComplete;
-
-	const addressLabel = prefersCollection
-		? // Translators: <address/> is the pickup location.
-		  __( 'Collection from <address/>', 'woocommerce' )
-		: deliversToLabel;
-
-	const title = (
-		<p className="wc-block-components-totals-shipping-address-summary">
-			{ !! formattedAddress && ! showEnterAddressMessage ? (
-				createInterpolateElement( addressLabel, {
-					address: <strong>{ formattedAddress }</strong>,
-				} )
-			) : (
-				<>
-					{ __(
-						'Enter address to check delivery options',
-						'woocommerce'
-					) }
-				</>
-			) }
-		</p>
-	);
-
-	return (
-		<div className="wc-block-components-shipping-address">
-			{ showCalculator && <ShippingCalculatorPanel title={ title } /> }
-		</div>
-	);
-};
-
-export default ShippingAddress;
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/test/index.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/test/index.tsx
index 7b788f38ea..5bb387b722 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/test/index.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/test/index.tsx
@@ -1,9 +1,8 @@
 /**
  * External dependencies
  */
-import { screen, render, within } from '@testing-library/react';
+import { screen, render } from '@testing-library/react';
 import { SlotFillProvider } from '@woocommerce/blocks-checkout';
-import { ShippingCalculatorContext } from '@woocommerce/base-components/cart-checkout/shipping-calculator/context';
 import * as wpData from '@wordpress/data';
 import { CartShippingRate } from '@woocommerce/types';
 import { previewCart as mockPreviewCart } from '@woocommerce/resource-previews';
@@ -88,7 +87,7 @@ const shippingRates = [
 						value: 'Test product &times; 1',
 					},
 				],
-				selected: false,
+				selected: true,
 				currency_code: 'USD',
 				currency_symbol: '$',
 				currency_minor_unit: 2,
@@ -184,128 +183,4 @@ describe( 'TotalsShipping', () => {
 		expect( screen.queryByText( 'Free' ) ).not.toBeInTheDocument();
 		expect( screen.getByText( '56.78' ) ).toBeInTheDocument();
 	} );
-
-	it( 'should show correct calculator panel label if address is complete', () => {
-		render(
-			<SlotFillProvider>
-				<ShippingCalculatorContext.Provider
-					value={ {
-						showCalculator: true,
-						isShippingCalculatorOpen: false,
-						setIsShippingCalculatorOpen: jest.fn(),
-						shippingCalculatorID:
-							'shipping-calculator-form-wrapper',
-					} }
-				>
-					<TotalsShipping />
-				</ShippingCalculatorContext.Provider>
-			</SlotFillProvider>
-		);
-
-		const panel = screen.getByRole( 'button' );
-		const paragraph = within( panel ).getByRole( 'paragraph' );
-
-		expect(
-			within( paragraph ).getByText( ( _, element ) => {
-				const text = element?.textContent || '';
-				return /Delivers to W1T 4JG, London, United Kingdom \(UK\)/.test(
-					text
-				);
-			} )
-		).toBeInTheDocument();
-	} );
-
-	it( 'should show correct calculator button label if address is incomplete', () => {
-		baseContextHooks.useStoreCart.mockReturnValue( {
-			...baseContextHooks.useStoreCart(),
-			shippingAddress: {
-				...shippingAddress,
-				city: '',
-				country: '',
-				postcode: '',
-			},
-		} );
-
-		render(
-			<SlotFillProvider>
-				<ShippingCalculatorContext.Provider
-					value={ {
-						showCalculator: true,
-						isShippingCalculatorOpen: false,
-						setIsShippingCalculatorOpen: jest.fn(),
-						shippingCalculatorID:
-							'shipping-calculator-form-wrapper',
-					} }
-				>
-					<TotalsShipping />
-				</ShippingCalculatorContext.Provider>
-			</SlotFillProvider>
-		);
-		expect(
-			screen.getByText( 'Enter address to check delivery options' )
-		).toBeInTheDocument();
-	} );
-
-	it( 'does show the calculator panel when default rates are available and has formatted address', () => {
-		baseContextHooks.useStoreCart.mockReturnValue( {
-			...baseContextHooks.useStoreCart(),
-			shippingAddress: {
-				...shippingAddress,
-				city: '',
-				state: 'California',
-				country: 'US',
-				postcode: '',
-			},
-		} );
-
-		render(
-			<SlotFillProvider>
-				<ShippingCalculatorContext.Provider
-					value={ {
-						showCalculator: true,
-						isShippingCalculatorOpen: false,
-						setIsShippingCalculatorOpen: jest.fn(),
-						shippingCalculatorID:
-							'shipping-calculator-form-wrapper',
-					} }
-				>
-					<TotalsShipping />
-				</ShippingCalculatorContext.Provider>
-			</SlotFillProvider>
-		);
-		expect(
-			screen.queryByText( 'Enter address to check delivery options' )
-		).not.toBeInTheDocument();
-	} );
-
-	it( 'should not show a calculator button label if no shipping methods exist', () => {
-		baseContextHooks.useStoreCart.mockReturnValue( {
-			...baseContextHooks.useStoreCart(),
-			shippingAddress: {
-				...shippingAddress,
-				city: '',
-				country: '',
-				postcode: '',
-			},
-		} );
-
-		render(
-			<SlotFillProvider>
-				<ShippingCalculatorContext.Provider
-					value={ {
-						showCalculator: false,
-						isShippingCalculatorOpen: false,
-						setIsShippingCalculatorOpen: jest.fn(),
-						shippingCalculatorID:
-							'shipping-calculator-form-wrapper',
-					} }
-				>
-					<TotalsShipping />
-				</ShippingCalculatorContext.Provider>
-			</SlotFillProvider>
-		);
-		expect(
-			screen.queryByText( 'Enter address to check delivery options' )
-		).not.toBeInTheDocument();
-	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/test/shipping-address.tsx b/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/test/shipping-address.tsx
deleted file mode 100644
index 95b68de959..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/base/components/cart-checkout/totals/shipping/test/shipping-address.tsx
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * External dependencies
- */
-import { render, screen, within } from '@testing-library/react';
-import ShippingAddress from '@woocommerce/base-components/cart-checkout/totals/shipping/shipping-address';
-import { cartStore, checkoutStore } from '@woocommerce/block-data';
-import { ShippingCalculatorContext } from '@woocommerce/base-components/cart-checkout';
-import { dispatch } from '@wordpress/data';
-import { previewCart } from '@woocommerce/resource-previews';
-import * as baseContextHooks from '@woocommerce/base-context/hooks';
-
-jest.mock( '@woocommerce/settings', () => {
-	const originalModule = jest.requireActual( '@woocommerce/settings' );
-
-	return {
-		// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-		// @ts-ignore We know @woocommerce/settings is an object.
-		...originalModule,
-		getSetting: ( setting: string, ...rest: unknown[] ) => {
-			if ( setting === 'localPickupEnabled' ) {
-				return true;
-			}
-			if ( setting === 'collectableMethodIds' ) {
-				return [ 'pickup_location' ];
-			}
-			if ( setting === 'localPickupLocations' ) {
-				return {
-					'1': {
-						enabled: true,
-						name: 'Local pickup #1',
-						formatted_address: '123 Easy Street',
-						details: 'Details for Local pickup #1',
-					},
-					'2': {
-						enabled: true,
-						name: 'Local pickup #2',
-						formatted_address: '456 Main St',
-						details: 'Details for Local pickup #2',
-					},
-				};
-			}
-			return originalModule.getSetting( setting, ...rest );
-		},
-	};
-} );
-
-jest.mock( '@woocommerce/base-context/hooks', () => {
-	return {
-		__esModule: true,
-		...jest.requireActual( '@woocommerce/base-context/hooks' ),
-		useStoreCart: jest.fn(),
-	};
-} );
-
-const shippingAddress = {
-	first_name: 'John',
-	last_name: 'Doe',
-	company: 'Automattic',
-	address_1: '123 Main St',
-	address_2: '',
-	city: 'San Francisco',
-	state: 'CA',
-	postcode: '94107',
-	country: 'US',
-	phone: '555-555-5555',
-};
-
-baseContextHooks.useStoreCart.mockReturnValue( {
-	cartItems: previewCart.items,
-	cartTotals: previewCart.totals,
-	cartCoupons: previewCart.coupons,
-	cartFees: previewCart.fees,
-	cartNeedsShipping: previewCart.needs_shipping,
-	shippingRates: previewCart.shipping_rates,
-	shippingAddress,
-	billingAddress: previewCart.billing_address,
-	cartHasCalculatedShipping: previewCart.has_calculated_shipping,
-	isLoadingRates: false,
-} );
-
-describe( 'ShippingAddress', () => {
-	it( 'Renders shipping address if user does not prefer collection', () => {
-		render(
-			<ShippingCalculatorContext.Provider
-				value={ {
-					showCalculator: true,
-					isShippingCalculatorOpen: false,
-					setIsShippingCalculatorOpen: jest.fn(),
-					shippingCalculatorID: 'shipping-calculator-form-wrapper',
-				} }
-			>
-				<ShippingAddress />
-			</ShippingCalculatorContext.Provider>
-		);
-
-		const panel = screen.getByRole( 'button' );
-		const paragraph = within( panel ).getByRole( 'paragraph' );
-
-		expect(
-			within( paragraph ).getByText( ( _, element ) => {
-				const text = element?.textContent || '';
-				return /Delivers to 94107/.test( text );
-			} )
-		).toBeInTheDocument();
-		expect(
-			screen.queryByText( /Collection from/ )
-		).not.toBeInTheDocument();
-		expect(
-			screen.queryByText( 'Enter address to check delivery options' )
-		).not.toBeInTheDocument();
-	} );
-
-	it( 'Renders pickup location if shopper prefers collection', async () => {
-		dispatch( checkoutStore ).setPrefersCollection( true );
-
-		// Deselect the default selected rate and select pickup_location:1 rate.
-		const currentlySelectedIndex =
-			previewCart.shipping_rates[ 0 ].shipping_rates.findIndex(
-				( rate ) => rate.selected
-			);
-		previewCart.shipping_rates[ 0 ].shipping_rates[
-			currentlySelectedIndex
-		].selected = false;
-		const pickupRateIndex =
-			previewCart.shipping_rates[ 0 ].shipping_rates.findIndex(
-				( rate ) => rate.method_id === 'pickup_location'
-			);
-		previewCart.shipping_rates[ 0 ].shipping_rates[
-			pickupRateIndex
-		].selected = true;
-
-		dispatch( cartStore ).receiveCart( previewCart );
-
-		render(
-			<ShippingCalculatorContext.Provider
-				value={ {
-					showCalculator: true,
-					isShippingCalculatorOpen: false,
-					setIsShippingCalculatorOpen: jest.fn(),
-					shippingCalculatorID: 'shipping-calculator-form-wrapper',
-				} }
-			>
-				<ShippingAddress />
-			</ShippingCalculatorContext.Provider>
-		);
-
-		const panel = screen.getByRole( 'button' );
-		const paragraph = within( panel ).getByRole( 'paragraph' );
-
-		expect(
-			within( paragraph ).getByText( ( _, element ) => {
-				const text = element?.textContent || '';
-				return /Collection from 123 Easy Street/.test( text );
-			} )
-		).toBeInTheDocument();
-	} );
-
-	it( `renders an address if one is set in the methods metadata`, async () => {
-		dispatch( checkoutStore ).setPrefersCollection( true );
-
-		// Deselect the default selected rate and select pickup_location:1 rate.
-		const currentlySelectedIndex =
-			previewCart.shipping_rates[ 0 ].shipping_rates.findIndex(
-				( rate ) => rate.selected
-			);
-		previewCart.shipping_rates[ 0 ].shipping_rates[
-			currentlySelectedIndex
-		].selected = false;
-		const pickupRateIndex =
-			previewCart.shipping_rates[ 0 ].shipping_rates.findIndex(
-				( rate ) => rate.method_id === 'pickup_location'
-			);
-		previewCart.shipping_rates[ 0 ].shipping_rates[
-			pickupRateIndex
-		].selected = true;
-
-		dispatch( cartStore ).receiveCart( previewCart );
-
-		render(
-			<ShippingCalculatorContext.Provider
-				value={ {
-					showCalculator: true,
-					isShippingCalculatorOpen: false,
-					setIsShippingCalculatorOpen: jest.fn(),
-					shippingCalculatorID: 'shipping-calculator-form-wrapper',
-				} }
-			>
-				<ShippingAddress />
-			</ShippingCalculatorContext.Provider>
-		);
-
-		const panel = screen.getByRole( 'button' );
-		const paragraph = within( panel ).getByRole( 'paragraph' );
-
-		expect(
-			within( paragraph ).getByText( ( _, element ) => {
-				const text = element?.textContent || '';
-				return /Collection from 123 Easy Street/.test( text );
-			} )
-		).toBeInTheDocument();
-	} );
-	it( 'renders no address if one is not set in the methods metadata', async () => {
-		dispatch( checkoutStore ).setPrefersCollection( true );
-
-		// Deselect the default selected rate and select pickup_location:1 rate.
-		const currentlySelectedIndex =
-			previewCart.shipping_rates[ 0 ].shipping_rates.findIndex(
-				( rate ) => rate.selected
-			);
-		previewCart.shipping_rates[ 0 ].shipping_rates[
-			currentlySelectedIndex
-		].selected = false;
-		const pickupRateIndex =
-			previewCart.shipping_rates[ 0 ].shipping_rates.findIndex(
-				( rate ) => rate.rate_id === 'pickup_location:2'
-			);
-		previewCart.shipping_rates[ 0 ].shipping_rates[
-			pickupRateIndex
-		].selected = true;
-
-		// Set the pickup_location metadata value to an empty string in the selected pickup rate.
-		const addressKeyIndex = previewCart.shipping_rates[ 0 ].shipping_rates[
-			pickupRateIndex
-		].meta_data.findIndex(
-			( metaData ) => metaData.key === 'pickup_address'
-		);
-		previewCart.shipping_rates[ 0 ].shipping_rates[
-			pickupRateIndex
-		].meta_data[ addressKeyIndex ].value = '';
-
-		dispatch( cartStore ).receiveCart( previewCart );
-
-		render( <ShippingAddress /> );
-		expect(
-			screen.queryByText( /Collection from / )
-		).not.toBeInTheDocument();
-	} );
-} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/utils/shipping-rates.ts b/plugins/woocommerce/client/blocks/assets/js/base/utils/shipping-rates.ts
index a34d7208e9..7e1ebfcada 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/utils/shipping-rates.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/utils/shipping-rates.ts
@@ -76,6 +76,17 @@ export const hasShippingRate = (
 	);
 };

+export const hasSelectedShippingRate = (
+	shippingRates: CartShippingRate[]
+): boolean => {
+	if ( ! hasShippingRate( shippingRates ) ) {
+		return false;
+	}
+	return shippingRates.some( ( shippingRatesPackage ) =>
+		shippingRatesPackage.shipping_rates.some( ( rate ) => rate.selected )
+	);
+};
+
 /**
  * Filters an array of packages/rates based on the shopper's preference for collection.
  */
@@ -122,11 +133,16 @@ export const getTotalShippingValue = ( values: {
 export const getSelectedShippingRateNames = (
 	shippingRates: CartShippingRate[]
 ): string[] => {
-	return shippingRates.flatMap( ( shippingPackage ) => {
-		return shippingPackage.shipping_rates
-			.filter( ( rate ) => rate.selected )
-			.flatMap( ( rate ) => rate.name );
-	} );
+	// This is to ensure we don't have duplicate rate names in the array.
+	return Array.from(
+		new Set(
+			shippingRates.flatMap( ( shippingPackage ) => {
+				return shippingPackage.shipping_rates
+					.filter( ( rate ) => rate.selected )
+					.map( ( rate ) => rate.name );
+			} )
+		)
+	);
 };

 export const selectedRatesAreCollectable = (
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/attributes.js b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/attributes.js
index 1d2ae1ed5b..fc0dd343c5 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/attributes.js
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/attributes.js
@@ -37,10 +37,6 @@ export const blockAttributes = {
 		default: getSetting( 'hasDarkEditorStyleSupport', false ),
 	},
 	// Deprecated - here for v1 migration support
-	isShippingCalculatorEnabled: {
-		type: 'boolean',
-		default: getSetting( 'isShippingCalculatorEnabled', true ),
-	},
 	checkoutPageId: {
 		type: 'number',
 		default: 0,
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-block/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-block/edit.tsx
index 965b255dd7..dc39eb254a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-block/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-block/edit.tsx
@@ -53,6 +53,7 @@ export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
 				<TotalsFooterItem
 					currency={ totalsCurrency }
 					values={ cartTotals }
+					isEstimate={ true }
 				/>
 			</div>
 			{ /* do I put an totals wrapper here? */ }
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-block/frontend.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-block/frontend.tsx
index c5b36fef47..7f3a4cd0c8 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-block/frontend.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-block/frontend.tsx
@@ -16,7 +16,7 @@ const FrontendBlock = ( {
 }: {
 	children?: JSX.Element | JSX.Element[];
 	className?: string;
-} ): JSX.Element | null => {
+} ) => {
 	const { cartTotals } = useStoreCart();
 	const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );

@@ -27,6 +27,7 @@ const FrontendBlock = ( {
 				<TotalsFooterItem
 					currency={ totalsCurrency }
 					values={ cartTotals }
+					isEstimate={ true }
 				/>
 			</div>
 			<OrderMetaSlotFill />
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-discount/block.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-discount/block.tsx
index c383d400fe..ae169b133c 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-discount/block.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-discount/block.tsx
@@ -23,9 +23,15 @@ const DiscountSlotFill = (): JSX.Element => {
 	return <ExperimentalDiscountsMeta.Slot { ...discountsSlotFillProps } />;
 };

-const Block = ( { className }: { className: string } ): JSX.Element => {
+const Block = ( { className }: { className: string } ) => {
 	const { cartTotals, cartCoupons } = useStoreCart();
 	const { removeCoupon, isRemovingCoupon } = useStoreCartCoupons( 'wc/cart' );
+
+	// Hide if there are no coupons to show.
+	if ( ! cartCoupons.length ) {
+		return null;
+	}
+
 	const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );

 	return (
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-fee/block.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-fee/block.tsx
index fbe49286d9..6412fe4901 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-fee/block.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-fee/block.tsx
@@ -5,8 +5,14 @@ import { TotalsFees, TotalsWrapper } from '@woocommerce/blocks-components';
 import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
 import { useStoreCart } from '@woocommerce/base-context/hooks';

-const Block = ( { className }: { className: string } ): JSX.Element => {
+const Block = ( { className }: { className: string } ) => {
 	const { cartFees, cartTotals } = useStoreCart();
+
+	// Hide if there are no fees to show.
+	if ( ! cartFees.length ) {
+		return null;
+	}
+
 	const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );

 	return (
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-shipping/block.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-shipping/block.tsx
index cf62160aa2..768f3aac20 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-shipping/block.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-shipping/block.tsx
@@ -2,87 +2,34 @@
  * External dependencies
  */
 import { __ } from '@wordpress/i18n';
-import { useState } from '@wordpress/element';
 import { TotalsShipping } from '@woocommerce/base-components/cart-checkout';
-import { ShippingCalculatorContext } from '@woocommerce/base-components/cart-checkout/shipping-calculator/context';
-import { useEditorContext, useStoreCart } from '@woocommerce/base-context';
+import { useStoreCart } from '@woocommerce/base-context';
 import { TotalsWrapper } from '@woocommerce/blocks-checkout';
-import {
-	getShippingRatesPackageCount,
-	selectedRatesAreCollectable,
-	allRatesAreCollectable,
-} from '@woocommerce/base-utils';
-import { getSetting } from '@woocommerce/settings';
-import { SHIPPING_METHODS_EXIST } from '@woocommerce/block-settings';
+import { hasSelectedShippingRate } from '@woocommerce/base-utils';

-/**
- * Internal dependencies
- */
-import { ShippingRateSelector } from './shipping-rate-selector';
-
-const Block = ( { className }: { className: string } ): JSX.Element | null => {
-	const { isEditor } = useEditorContext();
+const Block = ( { className }: { className: string } ) => {
 	const { cartNeedsShipping, shippingRates } = useStoreCart();
-	const [ isShippingCalculatorOpen, setIsShippingCalculatorOpen ] =
-		useState( false );

 	if ( ! cartNeedsShipping ) {
 		return null;
 	}

-	if ( isEditor && getShippingRatesPackageCount( shippingRates ) === 0 ) {
+	const hasSelectedRates = hasSelectedShippingRate( shippingRates );
+
+	if ( ! hasSelectedRates ) {
 		return null;
 	}

-	const showCalculator =
-		getSetting< boolean >( 'isShippingCalculatorEnabled', true ) &&
-		SHIPPING_METHODS_EXIST;
-
-	const hasSelectedCollectionOnly =
-		selectedRatesAreCollectable( shippingRates );
-
 	return (
 		<TotalsWrapper className={ className }>
-			<ShippingCalculatorContext.Provider
-				value={ {
-					showCalculator,
-					shippingCalculatorID: 'shipping-calculator-form-wrapper',
-					isShippingCalculatorOpen,
-					setIsShippingCalculatorOpen,
-				} }
-			>
-				<TotalsShipping
-					label={
-						hasSelectedCollectionOnly
-							? __( 'Pickup', 'woocommerce' )
-							: __( 'Delivery', 'woocommerce' )
-					}
-					placeholder={
-						! showCalculator ? (
-							<span className="wc-block-components-shipping-placeholder__value">
-								{ __(
-									'Calculated at checkout',
-									'woocommerce'
-								) }
-							</span>
-						) : null
-					}
-					collaterals={
-						<>
-							<ShippingRateSelector />
-							{ ! showCalculator &&
-								allRatesAreCollectable( shippingRates ) && (
-									<div className="wc-block-components-totals-shipping__delivery-options-notice">
-										{ __(
-											'Delivery options will be calculated during checkout',
-											'woocommerce'
-										) }
-									</div>
-								) }
-						</>
-					}
-				/>
-			</ShippingCalculatorContext.Provider>
+			<TotalsShipping
+				label={ __( 'Shipping', 'woocommerce' ) }
+				placeholder={
+					<span className="wc-block-components-shipping-placeholder__value">
+						{ __( 'Calculated at checkout', 'woocommerce' ) }
+					</span>
+				}
+			/>
 		</TotalsWrapper>
 	);
 };
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-shipping/shipping-rate-selector.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-shipping/shipping-rate-selector.tsx
deleted file mode 100644
index 70afb48b8e..0000000000
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-shipping/shipping-rate-selector.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * External dependencies
- */
-import { __ } from '@wordpress/i18n';
-import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout';
-import { useStoreCart } from '@woocommerce/base-context/hooks';
-
-export const ShippingRateSelector = () => {
-	const { shippingRates, isLoadingRates } = useStoreCart();
-
-	return (
-		<fieldset className="wc-block-components-totals-shipping__fieldset">
-			<legend className="screen-reader-text">
-				{ __( 'Shipping options', 'woocommerce' ) }
-			</legend>
-			<ShippingRatesControl
-				className="wc-block-components-totals-shipping__options"
-				shippingRates={ shippingRates }
-				isLoadingRates={ isLoadingRates }
-				context="woocommerce/cart"
-			/>
-		</fieldset>
-	);
-};
-
-export default ShippingRateSelector;
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-subtotal/block.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-subtotal/block.tsx
index 622a835c9c..fa1b0a797d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-subtotal/block.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-subtotal/block.tsx
@@ -5,8 +5,18 @@ import { Subtotal, TotalsWrapper } from '@woocommerce/blocks-components';
 import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
 import { useStoreCart } from '@woocommerce/base-context/hooks';

-const Block = ( { className = '' }: { className?: string } ): JSX.Element => {
+const Block = ( { className = '' }: { className?: string } ) => {
 	const { cartTotals } = useStoreCart();
+
+	// Hide if there are no other totals to show.
+	if (
+		! parseFloat( cartTotals.total_fees ) &&
+		! parseFloat( cartTotals.total_discount ) &&
+		! parseFloat( cartTotals.total_shipping )
+	) {
+		return null;
+	}
+
 	const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );

 	return (
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-taxes/block.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-taxes/block.tsx
index 1cc6e1f2fc..2843c11e4a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-taxes/block.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-taxes/block.tsx
@@ -12,7 +12,7 @@ const Block = ( {
 }: {
 	className: string;
 	showRateAfterTaxName: boolean;
-} ): JSX.Element | null => {
+} ) => {
 	const { cartTotals } = useStoreCart();

 	const displayCartPricesIncludingTax = getSetting(
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-totals/frontend.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-totals/frontend.tsx
index 95a97c552e..f2df63297d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-totals/frontend.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-totals/frontend.tsx
@@ -4,7 +4,7 @@ const FrontendBlock = ( {
 }: {
 	children?: JSX.Element | JSX.Element[];
 	className?: string;
-} ): JSX.Element | null => {
+} ) => {
 	return <div className={ className }>{ children }</div>;
 };

diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-totals/style.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-totals/style.scss
index 2bc5b77eaf..a1394b4ae3 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-totals/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/inner-blocks/cart-order-summary-totals/style.scss
@@ -1,6 +1,16 @@
 .wp-block-woocommerce-cart-order-summary-totals-block {
 	border-top: 1px solid $universal-border-light;
 	padding-bottom: $gap;
+
+	/**
+	* Hide the block if it has no children. `:empty` does not consider white-space, but this will change with CSS selectors level 4.
+	* `&:not(:has(*))` is a more robust selector that will work in all browsers.
+	*/
+	&:empty,
+	&:not(:has(*)) {
+		display: none;
+	}
+
 	.wc-block-components-totals-wrapper {
 		padding-bottom: 0;
 		border: 0;
@@ -9,4 +19,4 @@
 	.has-dark-controls & {
 		border-color: $input-border-dark;
 	}
-}
+}
\ No newline at end of file
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/style.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/style.scss
index 9fd2d8a1c7..d217749a7b 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/cart/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/cart/style.scss
@@ -1,10 +1,6 @@
 .wc-block-cart {
 	padding-top: $gap;

-	.wc-block-components-shipping-calculator {
-		white-space: nowrap;
-	}
-
 	.wc-block-components-address-form {
 		.wc-block-components-text-input,
 		.wc-block-components-country-input,
@@ -25,6 +21,18 @@
 	table.wc-block-cart-items td {
 		margin: 0 0 $gap * 2.5;
 	}
+
+	.wp-block-woocommerce-cart-order-summary-block {
+		border-bottom: 1px solid $universal-border-light;
+		margin-bottom: $gap;
+
+		.wc-block-components-totals-item,
+		.wc-block-components-panel,
+		.wc-block-components-totals-coupon {
+			padding-left: 0;
+			padding-right: 0;
+		}
+	}
 }

 // Loading placeholder state.
@@ -106,10 +114,6 @@
 	table.wc-block-cart-items {
 		margin: 0;
 	}
-	.wc-block-components-shipping-rates-control
-		.wc-block-components-radio-control__option {
-		padding-left: em($gap-huge);
-	}
 }

 .is-medium,
@@ -144,27 +148,6 @@

 	.wc-block-components-sidebar {
 		margin-top: 0;
-
-		.wc-block-components-shipping-calculator,
-		.wc-block-components-shipping-rates-control__package {
-			padding-left: 0;
-			padding-right: 0;
-		}
-
-		.wc-block-components-totals-item__description.wc-block-components-totals-shipping__via,
-		.wc-block-components-totals-shipping__change-address__link {
-			padding-top: $gap-smallest;
-		}
-
-		.wc-block-components-totals-shipping__options {
-			margin-top: $gap;
-		}
-		.wc-block-components-radio-control > * {
-			margin-bottom: $gap;
-		}
-		.wc-block-components-radio-control > *:last-child {
-			margin-bottom: 0;
-		}
 	}

 	.wc-block-cart__payment-options {
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/inner-blocks/checkout-order-summary-block/test/block.js b/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/inner-blocks/checkout-order-summary-block/test/block.js
index 8d73dfdc47..22209322e4 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/inner-blocks/checkout-order-summary-block/test/block.js
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/inner-blocks/checkout-order-summary-block/test/block.js
@@ -350,7 +350,7 @@ describe( 'Checkout Order Summary', () => {
 		setUseStoreCartReturnValue();
 		const { container } = render( <Block showRateAfterTaxName={ true } /> );
 		expect(
-			await findByText( container, 'Add a coupon' )
+			await findByText( container, 'Add coupons' )
 		).toBeInTheDocument();
 	} );

@@ -523,9 +523,7 @@ describe( 'Checkout Order Summary', () => {
 		expect(
 			await findByText(
 				container,
-				textContentMatcherAcrossSiblings(
-					'Delivery $40.00 Free shipping'
-				)
+				textContentMatcherAcrossSiblings( 'Free shipping $40.00' )
 			)
 		).toBeInTheDocument();
 	} );
diff --git a/plugins/woocommerce/client/blocks/assets/js/types/type-defs/cart-response.ts b/plugins/woocommerce/client/blocks/assets/js/types/type-defs/cart-response.ts
index 2c9ba9f450..1fbace3e93 100644
--- a/plugins/woocommerce/client/blocks/assets/js/types/type-defs/cart-response.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/types/type-defs/cart-response.ts
@@ -2,7 +2,7 @@
  * Internal dependencies
  */
 import type { CurrencyResponse } from './currency';
-import type { CartItem } from './cart';
+import type { CartItem, CartShippingRate } from './cart';
 import type { ProductResponseItem } from './product-response';

 export interface CartResponseTotalsItem extends CurrencyResponse {
@@ -66,14 +66,7 @@ export interface CartResponseShippingPackageShippingRate
 	selected: boolean;
 }

-export interface CartResponseShippingRate {
-	/* PackageId can be a string, WooCommerce Subscriptions uses strings for example, but WooCommerce core uses numbers */
-	package_id: number | string;
-	name: string;
-	destination: ResponseBaseAddress;
-	items: Array< ShippingRateItem >;
-	shipping_rates: Array< CartResponseShippingPackageShippingRate >;
-}
+export type CartResponseShippingRate = CartShippingRate;

 export interface CartResponseShippingAddress
 	extends ResponseBaseAddress,
diff --git a/plugins/woocommerce/client/blocks/packages/components/totals-wrapper/style.scss b/plugins/woocommerce/client/blocks/packages/components/totals-wrapper/style.scss
index cc93f91bfd..79507d4772 100644
--- a/plugins/woocommerce/client/blocks/packages/components/totals-wrapper/style.scss
+++ b/plugins/woocommerce/client/blocks/packages/components/totals-wrapper/style.scss
@@ -6,14 +6,10 @@
 		border-color: $input-border-dark;
 	}

-	&.has-bottom-border {
-		&::after {
-			border-bottom-width: 1px;
-		}
-	}
 	// TotalWrappers like Discount and Fee are sometimes empty
 	// this prevents displaying the empty areas in Order Summary
-	&:empty {
+	&:empty,
+	&:has(> *:only-child:empty) {
 		padding: 0;
 		border-width: 0;
 		&::after {
diff --git a/plugins/woocommerce/client/blocks/packages/components/totals/item/index.tsx b/plugins/woocommerce/client/blocks/packages/components/totals/item/index.tsx
index 08c0873bb8..4461651a50 100644
--- a/plugins/woocommerce/client/blocks/packages/components/totals/item/index.tsx
+++ b/plugins/woocommerce/client/blocks/packages/components/totals/item/index.tsx
@@ -14,7 +14,7 @@ import FormattedMonetaryAmount from '../../formatted-monetary-amount';

 export interface TotalsItemProps {
 	className?: string | undefined;
-	currency: Currency;
+	currency?: Currency | undefined;
 	label: string;
 	// Value may be a number, or react node. Numbers are passed to FormattedMonetaryAmount.
 	value: number | ReactNode;
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-block.merchant.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-block.merchant.block_theme.spec.ts
index 9df7d13463..fc5fbd49c2 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-block.merchant.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-block.merchant.block_theme.spec.ts
@@ -11,7 +11,6 @@ const blockData: BlockData = {
 		editor: {
 			block: '.wp-block-woocommerce-cart',
 			insertButton: "//button//span[text()='Cart']",
-			shippingBlock: '.wp-block-woocommerce-shipping-calculator',
 		},
 		frontend: {
 			block: '.wp-block-woocommerce-cart',
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-coupons.shopper.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-coupons.shopper.block_theme.spec.ts
index 1a8711d42f..641ef4cb9c 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-coupons.shopper.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-coupons.shopper.block_theme.spec.ts
@@ -34,7 +34,7 @@ test.describe( 'Shopper → Coupon', () => {
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
 		await frontendUtils.goToCart();

-		await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
+		await page.getByRole( 'button', { name: 'Add coupons' } ).click();
 		await page.getByLabel( 'Enter code' ).fill( 'single-use-coupon' );
 		await page.getByRole( 'button', { name: 'Apply' } ).click();

@@ -49,7 +49,7 @@ test.describe( 'Shopper → Coupon', () => {
 		).toBeHidden();

 		await frontendUtils.goToCheckout();
-		await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
+		await page.getByRole( 'button', { name: 'Add coupons' } ).click();
 		await page.getByLabel( 'Enter code' ).fill( 'single-use-coupon' );
 		await page.getByRole( 'button', { name: 'Apply' } ).click();

@@ -82,7 +82,7 @@ test.describe( 'Shopper → Coupon', () => {
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
 		await frontendUtils.goToCheckout();

-		await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
+		await page.getByRole( 'button', { name: 'Add coupons' } ).click();
 		await page.getByLabel( 'Enter code' ).fill( 'single-use-coupon' );
 		await page.getByRole( 'button', { name: 'Apply' } ).click();

@@ -98,7 +98,7 @@ test.describe( 'Shopper → Coupon', () => {
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
 		await frontendUtils.goToCheckout();

-		await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
+		await page.getByRole( 'button', { name: 'Add coupons' } ).click();
 		await page.getByLabel( 'Enter code' ).fill( 'single-use-coupon' );
 		await page.getByRole( 'button', { name: 'Apply' } ).click();

diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-shipping.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-shipping.block_theme.spec.ts
index 0942d69d90..57dd971301 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-shipping.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-shipping.block_theme.spec.ts
@@ -20,20 +20,14 @@ const test = base.extend< { checkoutPageObject: CheckoutPage } >( {
 } );

 test.describe( 'Merchant → Shipping', () => {
-	test( 'Merchant can enable shipping calculator and hide shipping costs before address is entered', async ( {
+	test( 'Merchant can hide shipping costs before address is entered', async ( {
 		page,
 		shippingUtils,
 		localPickupUtils,
 	} ) => {
 		await localPickupUtils.disableLocalPickup();
-
-		await shippingUtils.enableShippingCalculator();
 		await shippingUtils.enableShippingCostsRequireAddress();

-		await expect(
-			page.getByLabel( 'Enable the shipping calculator on the cart page' )
-		).toBeChecked();
-
 		await expect(
 			page.getByLabel( 'Hide shipping costs until an address is entered' )
 		).toBeChecked();
@@ -109,8 +103,10 @@ test.describe( 'Shopper → Shipping', () => {
 	test( '1. With shipping methods for the default location, shipping methods for _any_ location, and local pickup enabled, the shopper sees shipping rates and pickup options - rates are selected default', async ( {
 		localPickupUtils,
 		frontendUtils,
+		shippingUtils,
 	} ) => {
 		await localPickupUtils.enableLocalPickup();
+		await shippingUtils.disableShippingCostsRequireAddress();
 		await localPickupUtils.addPickupLocation( {
 			location: {
 				name: 'Automattic, Inc.',
@@ -124,19 +120,6 @@ test.describe( 'Shopper → Shipping', () => {

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
-		await expect(
-			frontendUtils.page.getByRole( 'radio', {
-				name: 'Flat rate shipping $',
-			} )
-		).toBeChecked();
-		await expect(
-			frontendUtils.page.getByRole( 'radio', {
-				name: 'Pickup (Automattic, Inc.) Free',
-			} )
-		).toBeVisible();
-
 		await frontendUtils.goToCheckout();
 		await expect(
 			frontendUtils.page.getByRole( 'radio', {
@@ -160,21 +143,12 @@ test.describe( 'Shopper → Shipping', () => {
 		localPickupUtils,
 		frontendUtils,
 		shippingUtils,
-		checkoutPageObject,
 	} ) => {
 		await localPickupUtils.disableLocalPickup();
 		await shippingUtils.disableShippingCostsRequireAddress();

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
-		await expect(
-			checkoutPageObject.page.getByRole( 'radio', {
-				name: 'Flat rate shipping $',
-			} )
-		).toBeChecked();
-
 		await frontendUtils.goToCheckout();
 		await expect(
 			frontendUtils.page.getByRole( 'radio', {
@@ -215,20 +189,6 @@ test.describe( 'Shopper → Shipping', () => {

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
-		await expect(
-			frontendUtils.page.getByText(
-				'Enter address to check delivery options'
-			)
-		).toBeHidden();
-
-		await expect(
-			frontendUtils.page.getByRole( 'radio', {
-				name: 'Flat rate shipping $',
-			} )
-		).toBeChecked();
-
 		await frontendUtils.goToCheckout();

 		await expect(
@@ -277,19 +237,6 @@ test.describe( 'Shopper → Shipping', () => {

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
-		await expect(
-			frontendUtils.page.getByText(
-				'Enter address to check delivery options'
-			)
-		).toBeHidden();
-
-		await expect(
-			frontendUtils.page.getByRole( 'radio', {
-				name: 'Pickup (Automattic, Inc.) Free',
-			} )
-		).toBeHidden();
 		await frontendUtils.goToCheckout();

 		await expect(
@@ -323,6 +270,7 @@ test.describe( 'Shopper → Shipping', () => {
 		localPickupUtils,
 		admin,
 		frontendUtils,
+		shippingUtils,
 	} ) => {
 		await admin.visitAdminPage( 'admin.php?page=wc-settings&tab=shipping' );
 		// Accept the delete dialog, then remove the listener;
@@ -332,6 +280,7 @@ test.describe( 'Shopper → Shipping', () => {
 		admin.page.off( 'dialog', acceptDialog );

 		await localPickupUtils.enableLocalPickup();
+		await shippingUtils.disableShippingCostsRequireAddress();
 		await localPickupUtils.addPickupLocation( {
 			location: {
 				name: 'Automattic, Inc.',
@@ -358,20 +307,6 @@ test.describe( 'Shopper → Shipping', () => {

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
-		await expect(
-			frontendUtils.page.getByText(
-				'Enter address to check delivery options'
-			)
-		).toBeHidden();
-
-		await expect(
-			frontendUtils.page.getByRole( 'radio', {
-				name: 'Pickup (Automattic, Inc.) Free',
-			} )
-		).toBeChecked();
-
 		await frontendUtils.goToCheckout();

 		await expect(
@@ -393,6 +328,7 @@ test.describe( 'Shopper → Shipping', () => {
 		localPickupUtils,
 		admin,
 		frontendUtils,
+		shippingUtils,
 	} ) => {
 		await admin.visitAdminPage( 'admin.php?page=wc-settings&tab=shipping' );
 		// Accept the delete dialog, then remove the listener;
@@ -402,6 +338,7 @@ test.describe( 'Shopper → Shipping', () => {
 		admin.page.off( 'dialog', acceptDialog );

 		await localPickupUtils.disableLocalPickup();
+		await shippingUtils.disableShippingCostsRequireAddress();

 		await admin.visitAdminPage( 'admin.php?page=wc-settings&tab=shipping' );

@@ -418,8 +355,6 @@ test.describe( 'Shopper → Shipping', () => {

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
 		await frontendUtils.goToCheckout();

 		await expect(
@@ -447,14 +382,6 @@ test.describe( 'Shopper → Shipping', () => {

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
-		await expect(
-			frontendUtils.page.getByText(
-				'Enter address to check delivery options'
-			)
-		).toBeVisible();
-
 		await frontendUtils.goToCheckout();

 		await expect(
@@ -504,14 +431,6 @@ test.describe( 'Shopper → Shipping', () => {

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
-		await expect(
-			frontendUtils.page.getByText(
-				'Enter address to check delivery options'
-			)
-		).toBeVisible();
-
 		await frontendUtils.goToCheckout();

 		await expect(
@@ -563,8 +482,6 @@ test.describe( 'Shopper → Shipping', () => {

 		await frontendUtils.goToShop();
 		await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await frontendUtils.goToCart();
-
 		await frontendUtils.goToCheckout();

 		await expect(
@@ -581,28 +498,6 @@ test.describe( 'Shopper → Shipping', () => {
 		).toBeHidden();
 	} );

-	test( 'Guest user can see shipping calculator on cart page', async ( {
-		requestUtils,
-		browser,
-	} ) => {
-		const guestContext = await browser.newContext( {
-			storageState: { cookies: [], origins: [] },
-		} );
-		const userPage = await guestContext.newPage();
-
-		const userFrontendUtils = new FrontendUtils( userPage, requestUtils );
-
-		await userFrontendUtils.goToShop();
-		await userFrontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-		await userFrontendUtils.goToCart();
-
-		// Note that the default customer location is set to the shop country/region, which
-		// is why this label is pre-populated with the shop country/region.
-		await expect(
-			userPage.getByText( 'Enter address to check delivery options' )
-		).toBeVisible();
-	} );
-
 	test( 'Guest user does not see shipping rates until full address is entered', async ( {
 		requestUtils,
 		browser,
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-translations.shopper.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-translations.shopper.block_theme.spec.ts
index 987fc121f3..d7ca48d50c 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-translations.shopper.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/cart/cart-checkout-block-translations.shopper.block_theme.spec.ts
@@ -49,7 +49,7 @@ test.describe( 'Shopper → Translations', () => {

 		await expect(
 			page.getByRole( 'button', {
-				name: getTestTranslation( 'Add a coupon' ),
+				name: getTestTranslation( 'Add coupons' ),
 			} )
 		).toBeVisible();

diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.spec.ts
index 5be4d059ce..cd961a0b02 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.spec.ts
@@ -228,11 +228,9 @@ test.describe( 'Shopper → Local pickup', () => {
 			'page=wc-settings&tab=shipping&section=options'
 		);

-		await expect(
-			admin.page.getByLabel(
-				'Hide shipping costs until an address is entered'
-			)
-		).toBeDisabled();
+		await admin.page
+			.getByLabel( 'Hide shipping costs until an address is entered' )
+			.uncheck();

 		let saveButton = admin.page.getByRole( 'button', {
 			name: 'Save changes',
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/shipping-utils.page.ts b/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/shipping-utils.page.ts
index 0dc658498d..8fd3e11775 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/shipping-utils.page.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/shipping-utils.page.ts
@@ -24,34 +24,6 @@ export class ShippingUtils {
 		await this.page.getByRole( 'button', { name: 'Save changes' } ).click();
 	}

-	async enableShippingCalculator() {
-		await this.openShippingSettings();
-
-		const enable = this.page.getByLabel(
-			'Enable the shipping calculator on the cart page'
-		);
-
-		if ( ! ( await enable.isChecked() ) ) {
-			await enable.check();
-
-			await this.saveShippingSettings();
-		}
-	}
-
-	async disableShippingCalculator() {
-		await this.openShippingSettings();
-
-		const enable = this.page.getByLabel(
-			'Enable the shipping calculator on the cart page'
-		);
-
-		if ( await enable.isChecked() ) {
-			await enable.uncheck();
-
-			await this.saveShippingSettings();
-		}
-	}
-
 	async enableShippingCostsRequireAddress() {
 		await this.openShippingSettings();

diff --git a/plugins/woocommerce/includes/class-wc-cart.php b/plugins/woocommerce/includes/class-wc-cart.php
index 8c9babf13d..27b2cbb571 100644
--- a/plugins/woocommerce/includes/class-wc-cart.php
+++ b/plugins/woocommerce/includes/class-wc-cart.php
@@ -15,6 +15,7 @@ use Automattic\WooCommerce\Enums\ProductType;
 use Automattic\WooCommerce\Utilities\DiscountsUtil;
 use Automattic\WooCommerce\Utilities\NumberUtil;
 use Automattic\WooCommerce\Utilities\ShippingUtil;
+use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;

 defined( 'ABSPATH' ) || exit;

@@ -1633,13 +1634,7 @@ class WC_Cart extends WC_Legacy_Cart {
 		}

 		if ( 'yes' === get_option( 'woocommerce_shipping_cost_requires_address' ) ) {
-			if ( 'store-api' === $this->cart_context ) {
-				$customer = $this->get_customer();
-
-				if ( ! $customer instanceof \WC_Customer || ! $customer->has_full_shipping_address() ) {
-					return false;
-				}
-			} else {
+			if ( 'shortcode' === $this->cart_context ) {
 				$country = $this->get_customer()->get_shipping_country();
 				if ( ! $country ) {
 					return false;
@@ -1677,6 +1672,17 @@ class WC_Cart extends WC_Legacy_Cart {
 				if ( $postcode_enabled && $postcode_required && '' === $this->get_customer()->get_shipping_postcode() && $checkout_postcode_field_exists ) {
 					return false;
 				}
+			} else {
+				// If local pickup is enabled, shipping should be shown so that pickup locations are visible before address entry.
+				if ( LocalPickupUtils::is_local_pickup_enabled() ) {
+					return true;
+				}
+
+				$customer = $this->get_customer();
+
+				if ( ! $customer instanceof \WC_Customer || ! $customer->has_full_shipping_address() ) {
+					return false;
+				}
 			}
 		}

diff --git a/plugins/woocommerce/includes/wc-cart-functions.php b/plugins/woocommerce/includes/wc-cart-functions.php
index 911f4c2215..b82eb2fbac 100644
--- a/plugins/woocommerce/includes/wc-cart-functions.php
+++ b/plugins/woocommerce/includes/wc-cart-functions.php
@@ -471,9 +471,24 @@ function wc_get_chosen_shipping_method_for_package( $key, $package ) {
  * @return string
  */
 function wc_get_default_shipping_method_for_package( $key, $package, $chosen_method ) {
-	$chosen_method_id     = current( explode( ':', $chosen_method ) );
-	$rate_keys            = array_keys( $package['rates'] );
-	$chosen_method_exists = in_array( $chosen_method, $rate_keys, true );
+	$rate_keys               = array_keys( $package['rates'] );
+	$local_pickup_method_ids = LocalPickupUtils::get_local_pickup_method_ids();
+
+	if ( 'shortcode' === WC()->cart->cart_context ) {
+		$default = current( $rate_keys );
+	} else {
+		// No default means that when you enter block checkout, shipping is chosen rather than pickup. We should only do this if there are shipping methods available other than local pickup.
+		$default = CartCheckoutUtils::shipping_methods_exist() ? '' : current( $rate_keys );
+
+		// Default to the first method in the package that isn't a local pickup method.
+		foreach ( $rate_keys as $rate_key ) {
+			$rate_method_id = current( explode( ':', $rate_key ) );
+			if ( ! in_array( $rate_method_id, $local_pickup_method_ids, true ) ) {
+				$default = $rate_key;
+				break;
+			}
+		}
+	}

 	/**
 	 * If the customer has selected local pickup, keep it selected if it's still in the package. We don't want to auto
@@ -481,11 +496,9 @@ function wc_get_default_shipping_method_for_package( $key, $package, $chosen_met
 	 *
 	 * This is important for block-based checkout where there is an explicit toggle between shipping and pickup.
 	 */
-	$local_pickup_method_ids = LocalPickupUtils::get_local_pickup_method_ids();
-	$is_local_pickup_chosen  = in_array( $chosen_method_id, $local_pickup_method_ids, true );
-
-	// Default to the first method in the package. This can be sorted in the backend by the merchant.
-	$default = current( $rate_keys );
+	$chosen_method_id       = current( explode( ':', $chosen_method ) );
+	$chosen_method_exists   = in_array( $chosen_method, $rate_keys, true );
+	$is_local_pickup_chosen = in_array( $chosen_method_id, $local_pickup_method_ids, true );

 	// Default to local pickup if its chosen already.
 	if ( $chosen_method_exists && $is_local_pickup_chosen ) {
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php b/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
index a049621525..c07d623da6 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/Cart.php
@@ -235,7 +235,6 @@ class Cart extends AbstractBlock {
 		parent::enqueue_data( $attributes );

 		$this->asset_data_registry->add( 'countryData', CartCheckoutUtils::get_country_data() );
-		$this->asset_data_registry->add( 'isShippingCalculatorEnabled', filter_var( get_option( 'woocommerce_enable_shipping_calc' ), FILTER_VALIDATE_BOOLEAN ) );
 		$this->asset_data_registry->add( 'displayItemizedTaxes', 'itemized' === get_option( 'woocommerce_tax_total_display' ) );
 		$this->asset_data_registry->add( 'displayCartPricesIncludingTax', 'incl' === get_option( 'woocommerce_tax_display_cart' ) );
 		$this->asset_data_registry->add( 'taxesEnabled', wc_tax_enabled() );
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/Checkout.php b/plugins/woocommerce/src/Blocks/BlockTypes/Checkout.php
index 67daa96be7..dc1d72ba82 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/Checkout.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/Checkout.php
@@ -448,7 +448,7 @@ class Checkout extends AbstractBlock {
 		$this->asset_data_registry->add( 'localPickupText', $pickup_location_settings['title'] );
 		$this->asset_data_registry->add( 'localPickupCost', $pickup_location_settings['cost'] );
 		$this->asset_data_registry->add( 'collectableMethodIds', $local_pickup_method_ids );
-		$this->asset_data_registry->add( 'shippingMethodsExist', CartCheckoutUtils::shipping_methods_exist() > 0 );
+		$this->asset_data_registry->add( 'shippingMethodsExist', CartCheckoutUtils::shipping_methods_exist() );

 		/**
 		 * This section handles the shipping address fields that trigger shipping rate recalculation.
diff --git a/plugins/woocommerce/src/Blocks/Shipping/ShippingController.php b/plugins/woocommerce/src/Blocks/Shipping/ShippingController.php
index 9df39b865f..1f0e38e9de 100644
--- a/plugins/woocommerce/src/Blocks/Shipping/ShippingController.php
+++ b/plugins/woocommerce/src/Blocks/Shipping/ShippingController.php
@@ -51,9 +51,8 @@ class ShippingController {
 	 * @param AssetDataRegistry $asset_data_registry Instance of the asset data registry.
 	 */
 	public function __construct( AssetApi $asset_api, AssetDataRegistry $asset_data_registry ) {
-		$this->asset_api           = $asset_api;
-		$this->asset_data_registry = $asset_data_registry;
-
+		$this->asset_api            = $asset_api;
+		$this->asset_data_registry  = $asset_data_registry;
 		$this->local_pickup_enabled = LocalPickupUtils::is_local_pickup_enabled();
 	}

@@ -83,27 +82,9 @@ class ShippingController {
 		add_filter( 'pre_update_option_pickup_location_pickup_locations', array( $this, 'flush_cache' ) );
 		add_filter( 'woocommerce_shipping_packages', array( $this, 'remove_shipping_if_no_address' ), 11 );
 		add_filter( 'woocommerce_order_shipping_to_display', array( $this, 'show_local_pickup_details' ), 10, 2 );
-
-		// This is required to short circuit `show_shipping` from class-wc-cart.php - without it, that function
-		// returns based on the option's value in the DB and we can't override it any other way.
-		add_filter( 'option_woocommerce_shipping_cost_requires_address', array( $this, 'override_cost_requires_address_option' ) );
 		add_action( 'rest_pre_serve_request', array( $this, 'track_local_pickup' ), 10, 4 );
 	}

-	/**
-	 * Overrides the option to force shipping calculations NOT to wait until an address is entered, but only if the
-	 * Checkout page contains the Checkout Block.
-	 *
-	 * @param boolean $value Whether shipping cost calculation requires address to be entered.
-	 * @return boolean Whether shipping cost calculation should require an address to be entered before calculating.
-	 */
-	public function override_cost_requires_address_option( $value ) {
-		if ( CartCheckoutUtils::is_checkout_block_default() && $this->local_pickup_enabled ) {
-			return 'no';
-		}
-		return $value;
-	}
-
 	/**
 	 * Inject collection details onto the order received page.
 	 *
@@ -222,13 +203,13 @@ class ShippingController {
 	 * @return array|mixed The filtered settings.
 	 */
 	public function remove_shipping_settings( $settings ) {
-		if ( CartCheckoutUtils::is_checkout_block_default() && $this->local_pickup_enabled ) {
+		if ( CartCheckoutUtils::is_cart_block_default() ) {
 			foreach ( $settings as $index => $setting ) {
-				if ( 'woocommerce_shipping_cost_requires_address' === $setting['id'] ) {
-					$settings[ $index ]['desc'] = sprintf(
+				if ( 'woocommerce_enable_shipping_calc' === $setting['id'] ) {
+					$settings[ $index ]['desc_tip'] = sprintf(
 					/* translators: %s: URL to the documentation. */
-						__( 'Hide shipping costs until an address is entered (Not available when using the <a href="%s">Local pickup options powered by the Checkout block</a>)', 'woocommerce' ),
-						'https://woocommerce.com/document/woocommerce-blocks-local-pickup/'
+						__( 'This feature is not available when using the <a href="%s">Cart and checkout blocks</a>. Shipping will be calculated at checkout.', 'woocommerce' ),
+						'https://woocommerce.com/document/woocommerce-store-editing/customizing-cart-and-checkout/'
 					);
 					$settings[ $index ]['disabled'] = true;
 					$settings[ $index ]['value']    = 'no';
@@ -527,10 +508,11 @@ class ShippingController {
 	}

 	/**
-	 * Remove shipping (i.e. delivery, not local pickup) if
-	 * "Hide shipping costs until an address is entered" is enabled,
+	 * Remove shipping (i.e. delivery, not local pickup) if "Hide shipping costs until an address is entered" is enabled,
 	 * and no address has been entered yet.
 	 *
+	 * Only applies to block checkout because pickup is chosen separately to shipping in that context.
+	 *
 	 * @param array $packages Array of shipping packages.
 	 * @return array
 	 */
@@ -566,6 +548,7 @@ class ShippingController {
 			$packages
 		);
 	}
+
 	/**
 	 * Track local pickup settings changes via Store API
 	 *
diff --git a/plugins/woocommerce/src/StoreApi/Schemas/V1/CartSchema.php b/plugins/woocommerce/src/StoreApi/Schemas/V1/CartSchema.php
index 31f91dc5c1..0fa8f6478f 100644
--- a/plugins/woocommerce/src/StoreApi/Schemas/V1/CartSchema.php
+++ b/plugins/woocommerce/src/StoreApi/Schemas/V1/CartSchema.php
@@ -4,7 +4,6 @@ namespace Automattic\WooCommerce\StoreApi\Schemas\V1;
 use Automattic\WooCommerce\StoreApi\SchemaController;
 use Automattic\WooCommerce\StoreApi\Utilities\CartController;
 use Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema;
-use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;
 use WC_Tax;

 /**
@@ -338,11 +337,8 @@ class CartSchema extends AbstractSchema {
 		// Get cart errors first so if recalculations are performed, it's reflected in the response.
 		$cart_errors = $this->get_cart_errors( $cart );

-		// Get shipping packages to return in the response from the cart. Always get the shipping packages if local
-		// pickup is enabled and has methods. If the address is required then regular shipping rates will be filtered
-		// out later.
-		$has_local_pickup_methods = LocalPickupUtils::is_local_pickup_enabled() && count( LocalPickupUtils::get_local_pickup_method_ids() ) > 0;
-		$shipping_packages        = $cart->has_calculated_shipping() || $has_local_pickup_methods ? $controller->get_shipping_packages() : [];
+		// Get shipping packages to return in the response from the cart.
+		$shipping_packages = $cart->has_calculated_shipping() ? $controller->get_shipping_packages() : [];

 		// Get visible cross sells products.
 		$cross_sells = array_filter( array_map( 'wc_get_product', $cart->get_cross_sells() ), 'wc_products_array_filter_visible' );
diff --git a/plugins/woocommerce/src/StoreApi/Utilities/CartController.php b/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
index 8a0715a89a..b855020961 100644
--- a/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
+++ b/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
@@ -28,13 +28,11 @@ class CartController {
 	 * Makes the cart and sessions available to a route by loading them from core.
 	 */
 	public function load_cart() {
-		if ( did_action( 'woocommerce_load_cart_from_session' ) ) {
-			return;
+		if ( ! did_action( 'woocommerce_load_cart_from_session' ) ) {
+			// Initialize the cart.
+			wc_load_cart();
 		}

-		// Initialize the cart.
-		wc_load_cart();
-
 		// Load cart from session.
 		$cart               = $this->get_cart_instance();
 		$cart->cart_context = 'store-api';
diff --git a/plugins/woocommerce/tests/e2e-pw/tests/api-tests/settings/settings-crud.test.js b/plugins/woocommerce/tests/e2e-pw/tests/api-tests/settings/settings-crud.test.js
index e6c9096735..c388e9ae34 100644
--- a/plugins/woocommerce/tests/e2e-pw/tests/api-tests/settings/settings-crud.test.js
+++ b/plugins/woocommerce/tests/e2e-pw/tests/api-tests/settings/settings-crud.test.js
@@ -1244,65 +1244,6 @@ test.describe( 'Settings API tests: CRUD', () => {
 					} ),
 				] )
 			);
-			expect( responseJSON ).toEqual(
-				expect.arrayContaining( [
-					expect.objectContaining( {
-						id: 'woocommerce_enable_shipping_calc',
-						label: 'Calculations',
-						description:
-							'Enable the shipping calculator on the cart page',
-						type: 'checkbox',
-						default: 'yes',
-						value: 'yes',
-					} ),
-				] )
-			);
-			expect( responseJSON ).toEqual(
-				expect.arrayContaining( [
-					expect.objectContaining( {
-						id: 'woocommerce_shipping_cost_requires_address',
-						label: '',
-						description:
-							'Hide shipping costs until an address is entered',
-						type: 'checkbox',
-						default: 'no',
-						value: 'no',
-					} ),
-				] )
-			);
-			expect( responseJSON ).toEqual(
-				expect.arrayContaining( [
-					expect.objectContaining( {
-						id: 'woocommerce_ship_to_destination',
-						label: 'Shipping destination',
-						description:
-							'This controls which shipping address is used by default.',
-						type: 'radio',
-						default: 'billing',
-						options: {
-							shipping: 'Default to customer shipping address',
-							billing: 'Default to customer billing address',
-							billing_only:
-								'Force shipping to the customer billing address',
-						},
-						tip: 'This controls which shipping address is used by default.',
-						value: 'billing',
-					} ),
-				] )
-			);
-			expect( responseJSON ).toEqual(
-				expect.arrayContaining( [
-					expect.objectContaining( {
-						id: 'woocommerce_shipping_debug_mode',
-						label: 'Debug mode',
-						description: 'Enable debug mode',
-						type: 'checkbox',
-						default: 'no',
-						tip: 'Enable shipping debug mode to show matching shipping zones and to bypass shipping rate cache.',
-						value: 'no',
-					} ),
-				] )
-			);
 		} );
 	} );

diff --git a/plugins/woocommerce/tests/e2e-pw/tests/coupons/cart-block-coupons.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/coupons/cart-block-coupons.spec.js
index 6f0bc9513a..98cda6cd2c 100644
--- a/plugins/woocommerce/tests/e2e-pw/tests/coupons/cart-block-coupons.spec.js
+++ b/plugins/woocommerce/tests/e2e-pw/tests/coupons/cart-block-coupons.spec.js
@@ -128,7 +128,7 @@ test.describe(
 				// apply all coupon types
 				for ( let i = 0; i < coupons.length; i++ ) {
 					await page
-						.getByRole( 'button', { name: 'Add a coupon' } )
+						.getByRole( 'button', { name: 'Add coupons' } )
 						.click();
 					await page
 						.getByLabel( 'Enter code' )
@@ -175,7 +175,7 @@ test.describe(
 				// add all coupons and verify prices
 				for ( let i = 0; i < coupons.length; i++ ) {
 					await page
-						.getByRole( 'button', { name: 'Add a coupon' } )
+						.getByRole( 'button', { name: 'Add coupons' } )
 						.click();
 					await page
 						.getByLabel( 'Enter code' )
@@ -221,7 +221,7 @@ test.describe(
 			async ( { page } ) => {
 				// try to add two same coupons and verify the error message
 				await page
-					.getByRole( 'button', { name: 'Add a coupon' } )
+					.getByRole( 'button', { name: 'Add coupons' } )
 					.click();
 				await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code );
 				await page.getByText( 'Apply', { exact: true } ).click();
@@ -235,7 +235,7 @@ test.describe(
 						)
 				).toBeVisible();
 				await page
-					.getByRole( 'button', { name: 'Add a coupon' } )
+					.getByRole( 'button', { name: 'Add coupons' } )
 					.click();
 				await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code );
 				await page.getByText( 'Apply', { exact: true } ).click();
@@ -255,7 +255,7 @@ test.describe(
 			async ( { page } ) => {
 				// add coupon with usage limit
 				await page
-					.getByRole( 'button', { name: 'Add a coupon' } )
+					.getByRole( 'button', { name: 'Add coupons' } )
 					.click();
 				await page.getByLabel( 'Enter code' ).fill( couponLimitedCode );
 				await page.getByText( 'Apply', { exact: true } ).click();
diff --git a/plugins/woocommerce/tests/e2e-pw/tests/coupons/checkout-block-coupons.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/coupons/checkout-block-coupons.spec.js
index d8d07414c1..59587384c7 100644
--- a/plugins/woocommerce/tests/e2e-pw/tests/coupons/checkout-block-coupons.spec.js
+++ b/plugins/woocommerce/tests/e2e-pw/tests/coupons/checkout-block-coupons.spec.js
@@ -129,7 +129,7 @@ test.describe(
 				// apply all coupon types
 				for ( let i = 0; i < coupons.length; i++ ) {
 					await page
-						.getByRole( 'button', { name: 'Add a coupon' } )
+						.getByRole( 'button', { name: 'Add coupons' } )
 						.click();
 					await page
 						.locator(
@@ -178,7 +178,7 @@ test.describe(
 				// add all coupons and verify prices
 				for ( let i = 0; i < coupons.length; i++ ) {
 					await page
-						.getByRole( 'button', { name: 'Add a coupon' } )
+						.getByRole( 'button', { name: 'Add coupons' } )
 						.click();
 					await page
 						.locator(
@@ -226,7 +226,7 @@ test.describe(
 			async ( { page } ) => {
 				// try to add two same coupons and verify the error message
 				await page
-					.getByRole( 'button', { name: 'Add a coupon' } )
+					.getByRole( 'button', { name: 'Add coupons' } )
 					.click();
 				await page
 					.locator(
@@ -244,7 +244,7 @@ test.describe(
 						)
 				).toBeVisible();
 				await page
-					.getByRole( 'button', { name: 'Add a coupon' } )
+					.getByRole( 'button', { name: 'Add coupons' } )
 					.click();
 				await page
 					.locator(
@@ -268,7 +268,7 @@ test.describe(
 			async ( { page } ) => {
 				// add coupon with usage limit
 				await page
-					.getByRole( 'button', { name: 'Add a coupon' } )
+					.getByRole( 'button', { name: 'Add coupons' } )
 					.click();
 				await page
 					.locator(
diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shipping/shipping-zones.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shipping/shipping-zones.spec.js
index fdec968686..b9ad1a1108 100644
--- a/plugins/woocommerce/tests/e2e-pw/tests/shipping/shipping-zones.spec.js
+++ b/plugins/woocommerce/tests/e2e-pw/tests/shipping/shipping-zones.spec.js
@@ -7,240 +7,13 @@ import { faker } from '@faker-js/faker';
  * Internal dependencies
  */
 import { ADMIN_STATE_PATH } from '../../playwright.config';
-import { expect, tags, test as baseTest } from '../../fixtures/fixtures';
-import { getFakeProduct } from '../../utils/data';
+import { expect, test as baseTest } from '../../fixtures/fixtures';
 import { WC_API_PATH } from '../../utils/api-client';
-import { updateIfNeeded, resetValue } from '../../utils/settings';

 function rand() {
 	return faker.string.alphanumeric( 5 );
 }

-/**
- * Checks the shipping rate in the cart.
- *
- * @param {import('@playwright/test').Page} page    - The Playwright Page object.
- * @param {Object}                          product - The product object.
- * @param {Object}                          checks  - The checks to perform on the shipping rate.
- * @return {Promise<boolean>} - Returns true if the shipping rate matches the expected rate, otherwise false.
- */
-async function checkShippingRateInCart( page, product, checks ) {
-	await page.context().clearCookies();
-
-	await page.goto( `shop/?add-to-cart=${ product.id }` );
-	await page.goto( 'cart/' );
-
-	await page
-		.getByRole( 'button', { name: 'Enter address to check' } )
-		.click();
-
-	await page.getByLabel( 'Country/Region' ).selectOption( checks.country );
-	await page.getByLabel( 'Province' ).selectOption( checks.state );
-	await page.getByRole( 'textbox', { name: 'City' } ).fill( checks.city );
-	await page
-		.getByRole( 'textbox', { name: 'Postal code' } )
-		.fill( checks.postCode );
-
-	await page
-		.getByRole( 'button', {
-			name: 'Check delivery options',
-			exact: true,
-		} )
-		.click();
-
-	await expect(
-		page.getByRole( 'radio', { name: checks.method } )
-	).toBeVisible();
-
-	if ( checks.cost ) {
-		await expect(
-			page.locator(
-				'.wc-block-components-shipping-rates-control__package .wc-block-components-formatted-money-amount'
-			)
-		).toContainText( checks.cost );
-	}
-
-	const total = checks.cost
-		? (
-				parseFloat( product.regular_price ) + parseFloat( checks.cost )
-		  ).toLocaleString( 'en-US', {
-				minimumFractionDigits: 2,
-				maximumFractionDigits: 2,
-		  } )
-		: product.regular_price;
-
-	await expect(
-		page.locator( '.wc-block-components-totals-item__value' ).last()
-	).toHaveText( `$${ total }` );
-}
-
-[
-	{
-		name: `BC with Free shipping ${ rand() }`,
-		zone: 'British Columbia, Canada',
-		postCode: '',
-		method: 'Free shipping',
-		checks: {
-			country: 'Canada',
-			state: 'British Columbia',
-			method: 'Free shipping',
-			postCode: 'V5K 0A1',
-			city: 'Vancouver',
-		},
-	},
-	{
-		name: `Canada with Flat rate ${ rand() }`,
-		zone: 'Canada',
-		postCode: '',
-		method: 'Flat rate',
-		cost: '15.00',
-		checks: {
-			country: 'Canada',
-			state: 'Alberta',
-			city: 'Calgary',
-			postCode: 'T2T 1B3',
-			method: 'Flat rate',
-			cost: '15.00',
-		},
-	},
-].forEach( ( { name, zone, postCode, method, cost, checks } ) => {
-	const test = baseTest.extend( {
-		storageState: ADMIN_STATE_PATH,
-		product: async ( { restApi }, use ) => {
-			let product = getFakeProduct();
-
-			await restApi
-				.post( `${ WC_API_PATH }/products`, product )
-				.then( ( response ) => {
-					product = response.data;
-				} );
-
-			await use( product );
-
-			await restApi.delete( `${ WC_API_PATH }/products/${ product.id }`, {
-				force: true,
-			} );
-		},
-		page: async ( { restApi, page }, use ) => {
-			const enableShippingCalcState = await updateIfNeeded(
-				'shipping/woocommerce_enable_shipping_calc',
-				'yes'
-			);
-
-			const shippingCostRequiresAddressState = await updateIfNeeded(
-				'shipping/woocommerce_shipping_cost_requires_address',
-				'yes'
-			);
-
-			const taxCalcState = await updateIfNeeded(
-				'general/woocommerce_calc_taxes',
-				'no'
-			);
-
-			await use( page );
-
-			// Cleanup
-			const allShippingZones = await restApi.get(
-				`${ WC_API_PATH }/shipping/zones`
-			);
-			for ( const shippingZone of allShippingZones.data ) {
-				if ( shippingZone.name === name ) {
-					await restApi
-						.delete(
-							`${ WC_API_PATH }/shipping/zones/${ shippingZone.id }`,
-							{
-								force: true,
-							}
-						)
-						.catch( ( error ) => {
-							console.error( error );
-						} );
-				}
-			}
-
-			await resetValue(
-				`shipping/woocommerce_enable_shipping_calc`,
-				enableShippingCalcState
-			);
-			await resetValue(
-				`shipping/woocommerce_shipping_cost_requires_address`,
-				shippingCostRequiresAddressState
-			);
-			await resetValue( `general/woocommerce_calc_taxes`, taxCalcState );
-		},
-	} );
-
-	test(
-		`can add and use shipping zone for ${ zone } with ${ method }`,
-		{ tag: [ tags.SERVICES ] },
-		async ( { page, product } ) => {
-			await page.goto(
-				'wp-admin/admin.php?page=wc-settings&tab=shipping&zone_id=new'
-			);
-			await page.getByPlaceholder( 'Zone name' ).fill( name );
-
-			const input = page.getByPlaceholder(
-				'Start typing to filter zones'
-			);
-			input.click();
-			input.fill( zone );
-
-			await page
-				.locator( 'label' )
-				.filter( { hasText: new RegExp( `^${ zone }$` ) } )
-				.click();
-
-			// Close dropdown
-			await page.getByPlaceholder( 'Zone name' ).click();
-
-			// Click limit to specific zip or post zone and fill it
-			await page.locator( '.wc-shipping-zone-postcodes-toggle' ).click();
-			await page
-				.getByPlaceholder( 'List 1 postcode per line' )
-				.fill( postCode );
-
-			await page
-				.getByRole( 'button', { name: 'Add shipping method' } )
-				.click();
-			await page.getByText( method, { exact: true } ).click();
-			await page
-				.getByRole( 'button', { name: 'Continue' } )
-				.last()
-				.click();
-
-			if ( cost ) {
-				await page
-					.locator( '#woocommerce_flat_rate_cost' )
-					.fill( cost );
-			}
-
-			await page.locator( '#btn-ok' ).click();
-
-			await expect(
-				page
-					.locator( '.wc-shipping-zone-method-title' )
-					.filter( { hasText: method } )
-			).toBeVisible();
-
-			await page.goto(
-				'wp-admin/admin.php?page=wc-settings&tab=shipping'
-			);
-
-			await expect( page.locator( '.wc-shipping-zones' ) ).toHaveText(
-				new RegExp( name )
-			);
-			await expect( page.locator( '.wc-shipping-zones' ) ).toHaveText(
-				new RegExp( `${ postCode }` )
-			);
-			await expect( page.locator( '.wc-shipping-zones' ) ).toHaveText(
-				new RegExp( method )
-			);
-
-			await checkShippingRateInCart( page, product, checks );
-		}
-	);
-} );
-
 const test = baseTest.extend( {
 	storageState: ADMIN_STATE_PATH,
 	zone: async ( { restApi }, use ) => {