Reinstate "Hide shipping costs until an address is entered" but disable it when using local pickup (https://github.com/woocommerce/woocommerce-blocks/pull/8964)

* Add class property to track local pickup enabled

* Force shipping enabled only when local pickup is also enabled

Otherwise, skip filtering and leave it to the current value

* Disabled and add text to WC Core hide shipping option

* Skip forcing shipping to be enabled in all cases

* Remove WC Core setting changes for hidden shipping rates option

* Add warning to local pickup UI about hidden rates setting in core

* Add local_pickup_enabled util function

* Revert "Skip forcing shipping to be enabled in all cases"

This reverts commit 0bf1886e73d791f7828ac86988f681cdce808b12.

* Check if local pickup is enabled before force enabling shipping

* Show correct shipping placeholder if rates hidden until address entered

* Remove tests for removed functionality

* Remove shippingCostRequiresAddress prop

* Update tests for shipping settings

* Remove irrelevant tests and fix existing ones

* Fix typo in comment

* Disable local pickup after each test

* Get shipping data from useCustomerData hook

* Change div in help prop to span

This prevents a DOM Nesting error, div cannot appear as a descendant of p

* Prevent hide shipping notice showing if the setting was originally off
This commit is contained in:
Thomas Roberts 2023-04-22 10:10:11 +01:00 committed by GitHub
parent 6903009af3
commit 30aecd2068
9 changed files with 127 additions and 329 deletions

View File

@ -2,7 +2,10 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useShippingData } from '@woocommerce/base-context/hooks';
import {
useCustomerData,
useShippingData,
} from '@woocommerce/base-context/hooks';
import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout';
import {
getShippingRatesPackageCount,
@ -19,15 +22,13 @@ import type {
PackageRateOption,
CartShippingPackageShippingRate,
} from '@woocommerce/types';
import { CART_STORE_KEY } from '@woocommerce/block-data';
import { useSelect } from '@wordpress/data';
import NoticeBanner from '@woocommerce/base-components/notice-banner';
import type { ReactElement } from 'react';
/**
* Internal dependencies
*/
import './style.scss';
import { shippingAddressHasValidationErrors } from '../../../../data/cart/utils';
/**
* Renders a shipping rate control option.
@ -54,10 +55,7 @@ const renderShippingRatesControlOption = (
};
};
const Block = ( {
noShippingPlaceholder = null,
shippingCostRequiresAddress = false,
} ): React.ReactElement | null => {
const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => {
const { isEditor } = useEditorContext();
const {
@ -68,9 +66,7 @@ const Block = ( {
isCollectable,
} = useShippingData();
const shippingAddressPushed = useSelect( ( select ) => {
return select( CART_STORE_KEY ).getFullShippingAddressPushed();
} );
const { shippingAddress } = useCustomerData();
const filteredShippingRates = isCollectable
? shippingRates.map( ( shippingRatesPackage ) => {
@ -86,25 +82,14 @@ const Block = ( {
} )
: shippingRates;
const shippingAddress = useSelect( ( select ) => {
return select( CART_STORE_KEY ).getCustomerData()?.shippingAddress;
} );
if ( ! needsShipping ) {
return null;
}
const shippingAddressHasErrors = ! shippingAddressHasValidationErrors();
const addressComplete = isAddressComplete( shippingAddress );
const shippingRatesPackageCount =
getShippingRatesPackageCount( shippingRates );
if (
( ! hasCalculatedShipping && ! shippingRatesPackageCount ) ||
( shippingCostRequiresAddress &&
( ! shippingAddressPushed || ! shippingAddressHasErrors ) )
) {
if ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) {
return (
<p>
{ __(
@ -114,6 +99,7 @@ const Block = ( {
</p>
);
}
const addressComplete = isAddressComplete( shippingAddress );
return (
<>

View File

@ -20,7 +20,6 @@ const FrontendBlock = ( {
showStepNumber,
children,
className,
shippingCostRequiresAddress = false,
}: {
title: string;
description: string;
@ -32,7 +31,6 @@ const FrontendBlock = ( {
showStepNumber: boolean;
children: JSX.Element;
className?: string;
shippingCostRequiresAddress: boolean;
} ) => {
const checkoutIsProcessing = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).isProcessing()
@ -55,9 +53,7 @@ const FrontendBlock = ( {
description={ description }
showStepNumber={ showStepNumber }
>
<Block
shippingCostRequiresAddress={ shippingCostRequiresAddress }
/>
<Block />
{ children }
</FormStep>
);

View File

@ -3,7 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import { createInterpolateElement, useState } from '@wordpress/element';
import { ADMIN_URL } from '@woocommerce/settings';
import { ADMIN_URL, getSetting } from '@woocommerce/settings';
import { CHECKOUT_PAGE_ID } from '@woocommerce/block-settings';
import {
CheckboxControl,
@ -47,6 +47,11 @@ const GeneralSettings = () => {
useSettingsContext();
const [ showCosts, setShowCosts ] = useState( !! settings.cost );
const shippingCostRequiresAddress = getSetting< boolean >(
'shippingCostRequiresAddress',
false
);
return (
<SettingsSection Description={ GeneralSettingsDescription }>
<SettingsCard>
@ -78,10 +83,23 @@ const GeneralSettings = () => {
'Enable local pickup',
'woo-gutenberg-products-block'
) }
help={ __(
'When enabled, local pickup will appear as an option on the block based checkout.',
'woo-gutenberg-products-block'
) }
help={
<span>
{ __(
'When enabled, local pickup will appear as an option on the block based checkout.',
'woo-gutenberg-products-block'
) }
{ shippingCostRequiresAddress ? (
<>
<br />
{ __(
'If local pickup is enabled, the "Hide shipping costs until an address is entered" setting will be ignored.',
'woo-gutenberg-products-block'
) }
</>
) : null }
</span>
}
/>
<TextControl
label={ __( 'Title', 'woo-gutenberg-products-block' ) }

View File

@ -27,6 +27,13 @@ class ShippingController {
*/
protected $asset_data_registry;
/**
* Whether local pickup is enabled.
*
* @var bool
*/
private $local_pickup_enabled;
/**
* Constructor.
*
@ -36,6 +43,8 @@ class ShippingController {
public function __construct( AssetApi $asset_api, AssetDataRegistry $asset_data_registry ) {
$this->asset_api = $asset_api;
$this->asset_data_registry = $asset_data_registry;
$this->local_pickup_enabled = LocalPickupUtils::is_local_pickup_enabled();
}
/**
@ -81,7 +90,7 @@ class ShippingController {
* @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() ) {
if ( CartCheckoutUtils::is_checkout_block_default() && $this->local_pickup_enabled ) {
return 'no';
}
return $value;
@ -94,7 +103,7 @@ class ShippingController {
* @return boolean Whether shipping should continue to be enabled/disabled.
*/
public function force_shipping_enabled( $enabled ) {
if ( CartCheckoutUtils::is_checkout_block_default() ) {
if ( CartCheckoutUtils::is_checkout_block_default() && $this->local_pickup_enabled ) {
return true;
}
return $enabled;
@ -141,48 +150,31 @@ class ShippingController {
*/
public function remove_shipping_settings( $settings ) {
// Do not add the "Hide shipping costs until an address is entered" setting if the Checkout block is not used on the WC checkout page.
if ( CartCheckoutUtils::is_checkout_block_default() ) {
$settings = array_filter(
$settings,
function( $setting ) {
return ! in_array(
$setting['id'],
array(
'woocommerce_shipping_cost_requires_address',
),
true
);
}
);
}
// Do not add the shipping calculator setting if the Cart block is not used on the WC cart page.
if ( CartCheckoutUtils::is_cart_block_default() ) {
// If the Cart is default, but not the checkout, we should ensure the 'Calculations' title is added to the
// `woocommerce_shipping_cost_requires_address` options group, since it is attached to the
// `woocommerce_enable_shipping_calc` option that we're going to remove later.
if ( ! CartCheckoutUtils::is_checkout_block_default() ) {
$calculations_title = '';
// Ensure the 'Calculations' title is added to the `woocommerce_shipping_cost_requires_address` options
// group, since it is attached to the `woocommerce_enable_shipping_calc` option that gets removed if the
// Cart block is in use.
$calculations_title = '';
// Get Calculations title so we can add it to 'Hide shipping costs until an address is entered' option.
foreach ( $settings as $setting ) {
if ( 'woocommerce_enable_shipping_calc' === $setting['id'] ) {
$calculations_title = $setting['title'];
break;
}
}
// Add Calculations title to 'Hide shipping costs until an address is entered' option.
foreach ( $settings as $index => $setting ) {
if ( 'woocommerce_shipping_cost_requires_address' === $setting['id'] ) {
$settings[ $index ]['title'] = $calculations_title;
$settings[ $index ]['checkboxgroup'] = 'start';
break;
}
// Get Calculations title so we can add it to 'Hide shipping costs until an address is entered' option.
foreach ( $settings as $setting ) {
if ( 'woocommerce_enable_shipping_calc' === $setting['id'] ) {
$calculations_title = $setting['title'];
break;
}
}
// Add Calculations title to 'Hide shipping costs until an address is entered' option.
foreach ( $settings as $index => $setting ) {
if ( 'woocommerce_shipping_cost_requires_address' === $setting['id'] ) {
$settings[ $index ]['title'] = $calculations_title;
$settings[ $index ]['checkboxgroup'] = 'start';
break;
}
}
$settings = array_filter(
$settings,
function( $setting ) {
@ -196,6 +188,18 @@ class ShippingController {
}
);
}
if ( CartCheckoutUtils::is_checkout_block_default() && $this->local_pickup_enabled ) {
foreach ( $settings as $index => $setting ) {
if ( 'woocommerce_shipping_cost_requires_address' === $setting['id'] ) {
$settings[ $index ]['desc'] .= ' (' . __( 'Not available when using WooCommerce Blocks Local Pickup', 'woo-gutenberg-products-block' ) . ')';
$settings[ $index ]['disabled'] = true;
$settings[ $index ]['value'] = 'no';
break;
}
}
}
return $settings;
}

View File

@ -6,6 +6,16 @@ namespace Automattic\WooCommerce\StoreApi\Utilities;
* the ShippingController, i.e. the OrderController.
*/
class LocalPickupUtils {
/**
* Checks if WC Blocks local pickup is enabled.
*
* @return bool True if local pickup is enabled.
*/
public static function is_local_pickup_enabled() {
$pickup_location_settings = get_option( 'woocommerce_pickup_location_settings', [] );
return wc_string_to_bool( $pickup_location_settings['enabled'] ?? 'no' );
}
/**
* Gets a list of payment method ids that support the 'local-pickup' feature.
*

View File

@ -22,7 +22,6 @@ import {
openWidgetEditor,
closeModalIfExists,
} from '../../utils.js';
import { merchant as merchantUtils } from '../../../utils/merchant';
const block = {
name: 'Checkout',
@ -128,79 +127,6 @@ describe( `${ block.name } Block`, () => {
await selectBlockByName( block.slug );
} );
it( 'can toggle "hide shipping costs until an address is entered"', async () => {
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
const toggleLabel = await findLabelWithText(
'Hide shipping costs until an address is entered'
);
await toggleLabel.click();
const shippingOptionsRequireAddressText = await page.$x(
'//p[contains(text(), "Shipping options will be displayed here after entering your full shipping address.")]'
);
await expect( shippingOptionsRequireAddressText ).toHaveLength(
1
);
await toggleLabel.click();
await expect( page ).toMatchElement(
'.wc-block-components-shipping-rates-control'
);
} );
it( 'toggles the same setting in shipping method and shipping methods blocks', async () => {
await merchantUtils.goToLocalPickupSettingsPage();
await merchantUtils.enableLocalPickup();
await merchantUtils.saveLocalPickupSettingsPageWithRefresh();
await visitBlockPage( `${ block.name } Block` );
await expect( page ).toClick(
'.wc-block-checkout__shipping-method button',
{ text: 'Shipping' }
);
await openSettingsSidebar();
const toggleLabel = await findLabelWithText(
'Hide shipping costs until an address is entered'
);
await toggleLabel.click();
const [ label ] = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
const shippingMethodForValue = await page.evaluate(
( passedLabel ) => passedLabel.getAttribute( 'for' ),
label
);
const shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
await expect( shippingMethodSettingIsChecked ).toBe( true );
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
const [ shippingMethodsLabel ] = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
const shippingMethodsLabelForValue = await page.evaluate(
( passedShippingMethodsLabel ) =>
passedShippingMethodsLabel.getAttribute( 'for' ),
shippingMethodsLabel
);
const shippingMethodLabelIsChecked = await page.evaluate(
( passedShippingMethodsLabelForValue ) =>
document.getElementById(
passedShippingMethodsLabelForValue
).checked,
shippingMethodsLabelForValue
);
expect( shippingMethodSettingIsChecked ).toBe(
shippingMethodLabelIsChecked
);
} );
it( 'can enable dark mode inputs', async () => {
const toggleLabel = await findLabelWithText(
'Dark mode inputs'

View File

@ -106,32 +106,8 @@ describe( `Local Pickup Settings`, () => {
checkoutSlug: 'checkout-block',
} );
} );
it( 'hides the correct shipping options if Checkout block is the default', async () => {
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=shipping&section=options'
);
const hideShippingLabel = await findLabelWithText(
'Hide shipping costs until an address is entered'
);
expect( hideShippingLabel ).toBeUndefined();
const shippingCalculatorLabel = await findLabelWithText(
'Enable the shipping calculator on the cart page'
);
expect( shippingCalculatorLabel ).toBeUndefined();
} );
it( 'does not hide the relevant setting if Cart or Checkout block is not the default', async () => {
await setCartCheckoutPages( {
cartSlug: 'cart',
checkoutSlug: 'checkout',
} );
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=advanced'
);
it( 'shows the correct shipping options depending on whether Local Pickup is enabled', async () => {
await merchant.disableLocalPickup();
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=shipping&section=options'
@ -141,10 +117,15 @@ describe( `Local Pickup Settings`, () => {
);
await expect( hideShippingLabel ).toHaveLength( 1 );
const shippingCalculatorLabel = await page.$x(
'//label[contains(., "Enable the shipping calculator on the cart page")]'
await merchant.enableLocalPickup();
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=shipping&section=options'
);
await expect( shippingCalculatorLabel ).toHaveLength( 1 );
const modifiedHideShippingLabel = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered (Not available when using WooCommerce Blocks Local Pickup)")]'
);
await expect( modifiedHideShippingLabel ).toHaveLength( 1 );
} );
} );

View File

@ -334,40 +334,8 @@ describe( 'Shopper → Checkout', () => {
const NORMAL_SHIPPING_NAME = 'Normal Shipping';
const NORMAL_SHIPPING_PRICE = '$20.00';
afterAll( async () => {
afterEach( async () => {
await merchant.login();
await visitBlockPage( 'Checkout Block' );
await openSettingsSidebar();
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
const [ label ] = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
const shippingMethodForValue = await page.evaluate(
( passedLabel ) => passedLabel.getAttribute( 'for' ),
label
);
let shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
if ( ! shippingMethodSettingIsChecked ) {
await setCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
}
shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
await merchantUtils.disableLocalPickup();
} );
@ -401,159 +369,68 @@ describe( 'Shopper → Checkout', () => {
await expect( page ).toMatch( NORMAL_SHIPPING_NAME );
} );
it( 'User sees the correct shipping options based on block settings', async () => {
await preventCompatibilityNotice();
await merchant.login();
await visitBlockPage( 'Checkout Block' );
await openSettingsSidebar();
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
const [ label ] = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
const shippingMethodForValue = await page.evaluate(
( passedLabel ) => passedLabel.getAttribute( 'for' ),
label
);
let shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
if ( ! shippingMethodSettingIsChecked ) {
await setCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
}
shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
await expect( shippingMethodSettingIsChecked ).toBe( true );
await saveOrPublish();
await shopper.block.emptyCart();
// Log out to have a fresh empty cart.
await shopper.logout();
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
await shopper.block.goToCheckout();
// Expect no shipping options to be shown, but with a friendly message.
const shippingOptionsRequireAddressText = await page.$x(
'//p[contains(text(), "Shipping options will be displayed here after entering your full shipping address.")]'
);
expect( shippingOptionsRequireAddressText ).toHaveLength( 1 );
// Enter the address and expect shipping options to be shown.
await shopper.block.fillInCheckoutWithTestData();
await expect( page ).toMatchElement(
'.wc-block-components-shipping-rates-control'
);
// This sequence will reset the checkout form.
await shopper.login();
await shopper.logout();
await preventCompatibilityNotice();
await merchant.login();
await visitBlockPage( 'Checkout Block' );
await openSettingsSidebar();
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
await unsetCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
await saveOrPublish();
await shopper.block.emptyCart();
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
await shopper.block.goToCheckout();
// Expect the shipping options to be displayed without entering an address.
await expect( page ).toMatchElement(
'.wc-block-components-shipping-rates-control'
);
} );
it( 'User does not see shipping rates until full address is entered', async () => {
await preventCompatibilityNotice();
await merchant.login();
await merchantUtils.enableLocalPickup();
await merchantUtils.addLocalPickupLocation();
await visitBlockPage( 'Checkout Block' );
await openSettingsSidebar();
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
await merchantUtils.disableLocalPickup();
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=shipping&section=options'
);
const hideShippingLabel = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
await hideShippingLabel[ 0 ].click();
await setCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
const saveButton = await page.$x(
'//button[contains(., "Save changes")]'
);
await saveOrPublish();
await saveButton[ 0 ].click();
await page.waitForXPath(
'//strong[contains(., "Your settings have been saved.")]'
);
await merchant.logout();
await shopper.block.emptyCart();
// Log out to have a fresh empty cart.
await shopper.logout();
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
await shopper.block.goToCheckout();
// Expect no shipping options to be shown, but with a friendly message.
// // Expect no shipping options to be shown, but with a friendly message.
const shippingOptionsRequireAddressText = await page.$x(
'//p[contains(text(), "Shipping options will be displayed here after entering your full shipping address.")]'
);
expect( shippingOptionsRequireAddressText ).toHaveLength( 1 );
await expect( page ).toClick(
'.wc-block-checkout__shipping-method button',
{ text: 'Shipping' }
);
await expect( shippingOptionsRequireAddressText ).toHaveLength( 1 );
// Enter the address but not city and expect shipping options not to be shown.
await shopper.block.fillInCheckoutWithTestData( { city: '' } );
await shopper.block.fillInCheckoutWithTestData( { postcode: '' } );
await expect( page ).not.toMatchElement(
'.wc-block-components-shipping-rates-control'
);
await merchant.login();
await merchantUtils.enableLocalPickup();
await merchantUtils.addLocalPickupLocation();
await merchant.logout();
// This sequence will reset the checkout form.
await shopper.login();
await shopper.logout();
await preventCompatibilityNotice();
await merchant.login();
await visitBlockPage( 'Checkout Block' );
await openSettingsSidebar();
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
await unsetCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
await saveOrPublish();
await shopper.block.emptyCart();
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
await shopper.block.goToCheckout();
await expect( page ).toClick(
'.wc-block-checkout__shipping-method button',
{ text: 'Shipping' }
);
// Expect the shipping options to be displayed without entering an address.
await expect( page ).toMatchElement(
'.wc-block-components-shipping-rates-control'

View File

@ -432,7 +432,7 @@ export const shopper = {
};
// We need to wait for the shipping total to update before we assert.
// As no dom elements are being added or removed, we cannot use `await page.waitForSelectot()`
// As no dom elements are being added or removed, we cannot use `await page.waitForSelector()`
// so instead we check when the `via <Shipping Method>` text changes
await page.$eval(
'.wc-block-components-totals-shipping .wc-block-components-totals-shipping__via',