Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Add setting to Checkout block to control Hide shipping costs until an address is entered option #8680

Merged
merged 54 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
e94143c
Force shipping to be enabled if the Checkout block is in use.
opr Mar 7, 2023
18af032
Add filter to override cost requires address option
opr Mar 7, 2023
e859292
Add shippingCostRequiresAddress option
opr Mar 7, 2023
871e6b5
Check if the address is required before showing rates
opr Mar 7, 2023
2815c96
Show shipping rates in editor
opr Mar 7, 2023
9b75f15
Add shippingCostRequiresAddress attribute to shipping methods block
opr Mar 7, 2023
dbb88e6
Update frontend type to show shippingCostRequiresAddress is a prop
opr Mar 7, 2023
0cd3111
Add control to toggle shippingCostRequiresAddress option
opr Mar 7, 2023
0ea6f94
Show address notice in the correct scenario
opr Mar 7, 2023
32c71f2
Send shippingCostRequiresAddress to Block in front end context
opr Mar 7, 2023
cec2a6d
Add e2e test for editor control
opr Mar 8, 2023
263211e
Add e2e tests for shipping options on the front end
opr Mar 8, 2023
ebc42ba
Add updateAttributeInSiblingBlock function
opr Mar 8, 2023
72c99b4
Add shippingCostRequiresAddress to shipping method block
opr Mar 8, 2023
ff01d03
Ensure attribute is updated in both blocks when editing
opr Mar 8, 2023
2f25f69
In Shipping Methods Block, show correct component based on block setting
opr Mar 8, 2023
f0c1fe7
Show correct block in editor
opr Mar 9, 2023
b5ea6dd
Remove broken test from PR
opr Mar 9, 2023
2516120
Add setCartCheckoutPages function to update set the cart/checkout page
opr Mar 7, 2023
8037e69
Add tests to ensure core shipping settings update correctly
opr Mar 7, 2023
9967dc0
Add isAddressComplete function
opr Mar 7, 2023
222e011
Check if the address is required before showing rates
opr Mar 7, 2023
633b32f
Show shipping rates in editor
opr Mar 7, 2023
c66c760
Show address notice in the correct scenario
opr Mar 7, 2023
a63b169
Add e2e tests for shipping options on the front end
opr Mar 8, 2023
704a49c
Ensure errorId is passed to StateInput
opr Mar 8, 2023
6ef99f3
Add fullShippingAddressPushed action to wc/store/cart
opr Mar 8, 2023
7dc347b
Add fullShippingAddressPushed case to reducer
opr Mar 8, 2023
cd18c23
Ensure fullShippingAddressPushed is set when initialising cart store
opr Mar 8, 2023
49ddfab
Add fullShippingAddressPushed selector and default state entry
opr Mar 8, 2023
4ea4107
Add shippingAddressHasValidationErrors util function
opr Mar 8, 2023
1234fa8
Do not overwrite addresses when selecting a rate
opr Mar 8, 2023
d12d1b2
Set whether full address has been pushed when saving address changes
opr Mar 8, 2023
5683e80
In Shipping Methods Block, show correct component based on block setting
opr Mar 8, 2023
a9ac41a
Don't show from price if rates should be hidden until address entered
opr Mar 8, 2023
953642f
Check city validation errors to assert if shipping address is valid
opr Mar 8, 2023
34bd979
Rename merchant.js to merchant.ts
opr Mar 8, 2023
8bb67dc
Move local pickup functions to common merchant util
opr Mar 8, 2023
8d7b1ce
Update local pickup tests to use common merchant utils
opr Mar 8, 2023
4da5fc6
Add test to ensure setting toggles in both blocks
opr Mar 8, 2023
afbf80a
Add navigating to settings and saving in merchant util
opr Mar 8, 2023
ae6b6c4
Create addPickupLocation merchant util
opr Mar 8, 2023
8e94d5a
Add test for local pickup and require full address
opr Mar 8, 2023
b7fd275
Make sure correct conditions are met to show shipping options
opr Mar 9, 2023
7c498e8
Clean up updateAttributeInSiblingBlock
opr Mar 9, 2023
496fe1f
Clean up updateAttributeInSiblingBlock
opr Mar 9, 2023
3791671
Ensure checkbox is checked during local pickup tests
opr Mar 9, 2023
5a54ccc
Unset the checkbox when tests are finished running
opr Mar 9, 2023
37b03f4
Update checkout block fixture
opr Mar 9, 2023
e3fd3d0
Prevent error in unit tests
opr Mar 9, 2023
f3ffa8c
Import validation store key from constants
opr Mar 9, 2023
c102fd4
Update checkout terms test to wait for button not to be disabled
opr Mar 9, 2023
484e56b
Revert "Add isAddressComplete function"
opr Mar 7, 2023
4a5c088
Merge pull request #8682 from woocommerce/add/hidden-shipping-data-store
mikejolley Mar 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@ export default {
remove: true,
},
},
shippingCostRequiresAddress: {
type: 'boolean',
default: false,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
"remove": true,
"move": true
}
},
"shippingCostRequiresAddress": {
"type": "boolean",
"default": false
}
},
"parent": [ "woocommerce/checkout-fields-block" ],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import { useDispatch, useSelect } from '@wordpress/data';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import ExternalLinkCard from '@woocommerce/editor-components/external-link-card';
import { Attributes } from '@woocommerce/blocks/checkout/types';
import { updateAttributeInSiblingBlock } from '@woocommerce/utils';

/**
* Internal dependencies
Expand Down Expand Up @@ -152,7 +154,9 @@ const ShippingSelector = ( {
export const Edit = ( {
attributes,
setAttributes,
clientId,
}: {
clientId: string;
attributes: {
title: string;
description: string;
Expand All @@ -163,9 +167,16 @@ export const Edit = ( {
showPrice: boolean;
showIcon: boolean;
className: string;
shippingCostRequiresAddress: boolean;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element | null => {
const toggleAttribute = ( key: keyof Attributes ): void => {
const newAttributes = {} as Partial< Attributes >;
newAttributes[ key ] = ! ( attributes[ key ] as boolean );
setAttributes( newAttributes );
};

const { setPrefersCollection } = useDispatch( CHECKOUT_STORE_KEY );
const { prefersCollection } = useSelect( ( select ) => {
const checkoutStore = select( CHECKOUT_STORE_KEY );
Expand Down Expand Up @@ -210,6 +221,30 @@ export const Edit = ( {
) }
>
<InspectorControls>
<PanelBody
title={ __(
'Calculations',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Hide shipping costs until an address is entered',
'woo-gutenberg-products-block'
) }
checked={ attributes.shippingCostRequiresAddress }
onChange={ ( selected ) => {
updateAttributeInSiblingBlock(
clientId,
'shippingCostRequiresAddress',
selected,
'woocommerce/checkout-shipping-methods-block'
);

toggleAttribute( 'shippingCostRequiresAddress' );
} }
/>
</PanelBody>
<PanelBody
title={ __( 'Appearance', 'woo-gutenberg-products-block' ) }
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ export default {
remove: true,
},
},
shippingCostRequiresAddress: {
type: 'boolean',
default: false,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
"remove": true,
"move": true
}
},
"shippingCostRequiresAddress": {
"type": "boolean",
"default": false
}
},
"parent": [ "woocommerce/checkout-fields-block" ],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ const renderShippingRatesControlOption = (
};
};

const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => {
const Block = ( {
noShippingPlaceholder = null,
shippingCostRequiresAddress = false,
} ): ReactElement | null => {
const { isEditor } = useEditorContext();

const {
Expand Down Expand Up @@ -85,9 +88,8 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => {
getShippingRatesPackageCount( shippingRates );

if (
! isEditor &&
! hasCalculatedShipping &&
! shippingRatesPackageCount
( ! hasCalculatedShipping && ! shippingRatesPackageCount ) ||
( shippingCostRequiresAddress && isEditor )
) {
return (
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import classnames from 'classnames';
import { __ } from '@wordpress/i18n';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { PanelBody, ExternalLink } from '@wordpress/components';
import { PanelBody, ExternalLink, ToggleControl } from '@wordpress/components';
import { ADMIN_URL, getSetting } from '@woocommerce/settings';
import ExternalLinkCard from '@woocommerce/editor-components/external-link-card';
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import { useCheckoutAddress } from '@woocommerce/base-context/hooks';
import Noninteractive from '@woocommerce/base-components/noninteractive';
import { Attributes } from '@woocommerce/blocks/checkout/types';
import { updateAttributeInSiblingBlock } from '@woocommerce/utils';

/**
* Internal dependencies
Expand All @@ -32,12 +34,15 @@ type shippingAdminLink = {
export const Edit = ( {
attributes,
setAttributes,
clientId,
}: {
clientId: string;
attributes: {
title: string;
description: string;
showStepNumber: boolean;
className: string;
shippingCostRequiresAddress: boolean;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element | null => {
Expand All @@ -54,6 +59,12 @@ export const Edit = ( {
return null;
}

const toggleAttribute = ( key: keyof Attributes ): void => {
const newAttributes = {} as Partial< Attributes >;
newAttributes[ key ] = ! ( attributes[ key ] as boolean );
setAttributes( newAttributes );
};

return (
<FormStepBlock
attributes={ attributes }
Expand All @@ -64,6 +75,29 @@ export const Edit = ( {
) }
>
<InspectorControls>
<PanelBody
title={ __(
'Calculations',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Hide shipping costs until an address is entered',
'woo-gutenberg-products-block'
) }
checked={ attributes.shippingCostRequiresAddress }
onChange={ ( selected ) => {
updateAttributeInSiblingBlock(
clientId,
'shippingCostRequiresAddress',
selected,
'woocommerce/checkout-shipping-method-block'
);
toggleAttribute( 'shippingCostRequiresAddress' );
} }
/>
</PanelBody>
{ globalShippingMethods.length > 0 && (
<PanelBody
title={ __(
Expand Down Expand Up @@ -129,7 +163,12 @@ export const Edit = ( {
) }
</InspectorControls>
<Noninteractive>
<Block noShippingPlaceholder={ <NoShippingPlaceholder /> } />
<Block
noShippingPlaceholder={ <NoShippingPlaceholder /> }
shippingCostRequiresAddress={
attributes.shippingCostRequiresAddress
}
/>
</Noninteractive>
<AdditionalFields block={ innerBlockAreas.SHIPPING_METHODS } />
</FormStepBlock>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const FrontendBlock = ( {
showStepNumber,
children,
className,
shippingCostRequiresAddress = false,
}: {
title: string;
description: string;
Expand All @@ -31,6 +32,7 @@ const FrontendBlock = ( {
showStepNumber: boolean;
children: JSX.Element;
className?: string;
shippingCostRequiresAddress: boolean;
} ) => {
const checkoutIsProcessing = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).isProcessing()
Expand All @@ -53,7 +55,9 @@ const FrontendBlock = ( {
description={ description }
showStepNumber={ showStepNumber }
>
<Block />
<Block
shippingCostRequiresAddress={ shippingCostRequiresAddress }
/>
{ children }
</FormStep>
);
Expand Down
33 changes: 33 additions & 0 deletions assets/js/utils/attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import { getSetting } from '@woocommerce/settings';
import { AttributeObject, AttributeSetting } from '@woocommerce/types';
import { dispatch, select } from '@wordpress/data';

const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] );

Expand Down Expand Up @@ -82,3 +83,35 @@ export const getTaxonomyFromAttributeId = ( attributeId: number ) => {
const attribute = getAttributeFromID( attributeId );
return attribute ? attribute.taxonomy : null;
};

/**
* Updates an attribute in a sibling block. Useful if two settings control the same attribute, but you don't want to
* have this attribute exist on a parent block.
*/
export const updateAttributeInSiblingBlock = (
clientId: string,
attribute: string,
newValue: unknown,
siblingBlockName: string
) => {
const store = select( 'core/block-editor' );
const actions = dispatch( 'core/block-editor' );
const parentBlocks = store.getBlockParents( clientId );

let shippingMethodsBlockClientId = '';
// Loop through parent block's children until we find woocommerce/checkout-shipping-methods-block.
// Also set this attribute in the woocommerce/checkout-shipping-methods-block.
parentBlocks.forEach( ( parent ) => {
const childBlock = store
.getBlock( parent )
.innerBlocks.find( ( child ) => child.name === siblingBlockName );
if ( ! childBlock ) {
return;
}
//console.log( childBlock );
opr marked this conversation as resolved.
Show resolved Hide resolved
shippingMethodsBlockClientId = childBlock.clientId;
} );
actions.updateBlockAttributes( shippingMethodsBlockClientId, {
[ attribute ]: newValue,
} );
};
34 changes: 34 additions & 0 deletions src/Shipping/ShippingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
use Automattic\WooCommerce\Blocks\Tests\BlockTypes\Cart;
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;
use Automattic\WooCommerce\Utilities\ArrayUtil;
Expand Down Expand Up @@ -52,6 +53,7 @@ function() {
);
}
$this->asset_data_registry->add( 'collectableMethodIds', array( 'Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils', 'get_local_pickup_method_ids' ), true );
$this->asset_data_registry->add( 'shippingCostRequiresAddress', get_option( 'woocommerce_shipping_cost_requires_address', false ) === 'yes' );
add_action( 'rest_api_init', [ $this, 'register_settings' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'hydrate_client_settings' ] );
Expand All @@ -62,6 +64,38 @@ function() {
add_filter( 'pre_update_option_woocommerce_pickup_location_settings', array( $this, 'flush_cache' ) );
add_filter( 'pre_update_option_pickup_location_pickup_locations', array( $this, 'flush_cache' ) );
add_filter( 'woocommerce_shipping_settings', array( $this, 'remove_shipping_settings' ) );
add_filter( 'wc_shipping_enabled', array( $this, 'force_shipping_enabled' ), 100, 1 );

// 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' ) );
}

/**
* 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() ) {
return 'no';
}
return $value;
}

/**
* Force shipping to be enabled if the Checkout block is in use on the Checkout page.
*
* @param boolean $enabled Whether shipping is currently enabled.
* @return boolean Whether shipping should continue to be enabled/disabled.
*/
public function force_shipping_enabled( $enabled ) {
if ( CartCheckoutUtils::is_checkout_block_default() ) {
return true;
}
return $enabled;
}

/**
Expand Down
21 changes: 21 additions & 0 deletions tests/e2e/specs/backend/checkout.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,27 @@ 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( 'can enable dark mode inputs', async () => {
const toggleLabel = await findLabelWithText(
'Dark mode inputs'
Expand Down