Checkout Block: Avoid using editor buttons in the post content to prevent inheriting global styles (https://github.com/woocommerce/woocommerce-blocks/pull/8957)

* Remove express payment placeholders when there are no express payment methods

* Only use one instance of CartCheckoutFeedbackPrompt

* Show NoPaymentMethods notice in the inspector instead of the block

* Move privacy notices to inspector

* Unused hook

* Update tests to use mocks
This commit is contained in:
Mike Jolley 2023-04-06 12:16:42 +01:00 committed by GitHub
parent e977882a74
commit 62c62d3326
13 changed files with 204 additions and 369 deletions

View File

@ -2,10 +2,6 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Placeholder, Button } from 'wordpress-components';
import { Icon, payment } from '@wordpress/icons';
import { ADMIN_URL } from '@woocommerce/settings';
import { useEditorContext } from '@woocommerce/base-context';
import NoticeBanner from '@woocommerce/base-components/notice-banner';
/**
@ -17,50 +13,6 @@ import './style.scss';
* Render content when no payment methods are found depending on context.
*/
const NoPaymentMethods = () => {
const { isEditor } = useEditorContext();
return isEditor ? (
<NoPaymentMethodsPlaceholder />
) : (
<NoPaymentMethodsNotice />
);
};
/**
* Renders a placeholder in the editor.
*/
const NoPaymentMethodsPlaceholder = () => {
return (
<Placeholder
icon={ <Icon icon={ payment } /> }
label={ __( 'Payment methods', 'woo-gutenberg-products-block' ) }
className="wc-block-checkout__no-payment-methods-placeholder"
>
<span className="wc-block-checkout__no-payment-methods-placeholder-description">
{ __(
'Your store does not have any payment methods that support the Checkout block. Once you have configured a compatible payment method it will be displayed here.',
'woo-gutenberg-products-block'
) }
</span>
<Button
isSecondary
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout` }
target="_blank"
rel="noopener noreferrer"
>
{ __(
'Configure Payment Methods',
'woo-gutenberg-products-block'
) }
</Button>
</Placeholder>
);
};
/**
* Renders a notice on the frontend.
*/
const NoPaymentMethodsNotice = () => {
return (
<NoticeBanner
isDismissible={ false }

View File

@ -1,25 +1,3 @@
.components-placeholder.wc-block-checkout__no-payment-methods-placeholder {
margin-bottom: $gap;
* {
pointer-events: all; // Overrides parent disabled component in editor context
}
.components-placeholder__fieldset {
display: block;
.components-button {
background-color: $gray-900;
color: $white;
}
.wc-block-checkout__no-payment-methods-placeholder-description {
display: block;
margin: 0.25em 0 1em 0;
}
}
}
.components-notice.wc-block-checkout__no-payment-methods-notice {
margin-bottom: $gap;
margin: 0 0 $gap;
}

View File

@ -9,14 +9,12 @@ import {
import { addFilter, hasFilter } from '@wordpress/hooks';
import type { StoreDescriptor } from '@wordpress/data';
import { CartCheckoutSidebarCompatibilityNotice } from '@woocommerce/editor-components/sidebar-compatibility-notice';
import {
DefaultNotice,
LegacyNotice,
} from '@woocommerce/editor-components/default-notice';
import { NoPaymentMethodsNotice } from '@woocommerce/editor-components/no-payment-methods-notice';
import { PAYMENT_STORE_KEY } from '@woocommerce/block-data';
import { DefaultNotice } from '@woocommerce/editor-components/default-notice';
import { IncompatiblePaymentGatewaysNotice } from '@woocommerce/editor-components/incompatible-payment-gateways-notice';
import { useSelect } from '@wordpress/data';
import { CartCheckoutFeedbackPrompt } from '@woocommerce/editor-components/feedback-prompt';
import { isWcVersion } from '@woocommerce/settings';
import { useState } from '@wordpress/element';
declare module '@wordpress/editor' {
let store: StoreDescriptor;
@ -32,7 +30,11 @@ declare module '@wordpress/block-editor' {
const withSidebarNotices = createHigherOrderComponent(
( BlockEdit ) => ( props ) => {
const { name: blockName, isSelected: isBlockSelected } = props;
const {
clientId,
name: blockName,
isSelected: isBlockSelected,
} = props;
const [
isIncompatiblePaymentGatewaysNoticeDismissed,
@ -45,15 +47,8 @@ const withSidebarNotices = createHigherOrderComponent(
setIsIncompatiblePaymentGatewaysNoticeDismissed( isDismissed );
};
const addressFieldOrAccountBlocks = [
'woocommerce/checkout-shipping-address-block',
'woocommerce/checkout-billing-address-block',
'woocommerce/checkout-contact-information-block',
'woocommerce/checkout-fields-block',
];
const { clientId } = props;
const { isCart, isCheckout, isAddressFieldBlock } = useSelect(
( select ) => {
const { isCart, isCheckout, isPaymentMethodsBlock, hasPaymentMethods } =
useSelect( ( select ) => {
const { getBlockParentsByBlockName, getBlockName } =
select( blockEditorStore );
const parent = getBlockParentsByBlockName( clientId, [
@ -68,58 +63,61 @@ const withSidebarNotices = createHigherOrderComponent(
isCheckout:
parent.includes( 'woocommerce/checkout' ) ||
currentBlockName === 'woocommerce/checkout',
isAddressFieldBlock:
addressFieldOrAccountBlocks.includes(
currentBlockName
),
isPaymentMethodsBlock:
currentBlockName ===
'woocommerce/checkout-payment-block',
hasPaymentMethods:
select(
PAYMENT_STORE_KEY
).paymentMethodsInitialized() &&
Object.keys(
select(
PAYMENT_STORE_KEY
).getAvailablePaymentMethods()
).length > 0,
};
}
);
const MakeAsDefaultNotice = () => (
<>
{ isWcVersion( '6.9.0', '>=' ) ? (
<DefaultNotice block={ isCheckout ? 'checkout' : 'cart' } />
) : (
<LegacyNotice block={ isCheckout ? 'checkout' : 'cart' } />
) }
</>
);
} );
// Show sidebar notices only when a WooCommerce block is selected.
if ( ! blockName.startsWith( 'woocommerce/' ) || ! isBlockSelected ) {
if (
! blockName.startsWith( 'woocommerce/' ) ||
! isBlockSelected ||
! ( isCart || isCheckout )
) {
return <BlockEdit key="edit" { ...props } />;
}
return (
<>
{ ( isCart || isCheckout ) && (
<InspectorControls>
<IncompatiblePaymentGatewaysNotice
toggleDismissedStatus={
toggleIncompatiblePaymentGatewaysNoticeDismissedStatus
}
block={
isCheckout
? 'woocommerce/checkout'
: 'woocommerce/cart'
}
/>
<InspectorControls>
<IncompatiblePaymentGatewaysNotice
toggleDismissedStatus={
toggleIncompatiblePaymentGatewaysNoticeDismissedStatus
}
block={
isCheckout
? 'woocommerce/checkout'
: 'woocommerce/cart'
}
/>
{ isIncompatiblePaymentGatewaysNoticeDismissed ? (
<>
<MakeAsDefaultNotice />
<CartCheckoutSidebarCompatibilityNotice
block={ isCheckout ? 'checkout' : 'cart' }
/>
</>
) : null }
{ isIncompatiblePaymentGatewaysNoticeDismissed ? (
<>
<DefaultNotice
block={ isCheckout ? 'checkout' : 'cart' }
/>
<CartCheckoutSidebarCompatibilityNotice
block={ isCheckout ? 'checkout' : 'cart' }
/>
</>
) : null }
{ isAddressFieldBlock ? null : (
<CartCheckoutFeedbackPrompt />
) }
</InspectorControls>
) }
{ isPaymentMethodsBlock && ! hasPaymentMethods && (
<NoPaymentMethodsNotice />
) }
<CartCheckoutFeedbackPrompt />
</InspectorControls>
<BlockEdit key="edit" { ...props } />
</>
);

View File

@ -1,12 +1,8 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { Placeholder, Button } from '@wordpress/components';
import { useExpressPaymentMethods } from '@woocommerce/base-context/hooks';
import { Icon, payment } from '@wordpress/icons';
import { ADMIN_URL } from '@woocommerce/settings';
import classnames from 'classnames';
/**
@ -15,38 +11,6 @@ import classnames from 'classnames';
import Block from './block';
import './editor.scss';
/**
* Renders a placeholder in the editor.
*/
const NoExpressPaymentMethodsPlaceholder = () => {
return (
<Placeholder
icon={ <Icon icon={ payment } /> }
label={ __( 'Express Checkout', 'woo-gutenberg-products-block' ) }
className="wp-block-woocommerce-checkout-express-payment-block-placeholder"
>
<span className="wp-block-woocommerce-checkout-express-payment-block-placeholder__description">
{ __(
'Your store does not have any payment methods that support the Express Checkout block. Once you have configured a compatible payment method, it will be displayed here.',
'woo-gutenberg-products-block'
) }
</span>
<Button
isPrimary
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout` }
target="_blank"
rel="noopener noreferrer"
className="wp-block-woocommerce-checkout-express-payment-block-placeholder__button"
>
{ __(
'Configure Payment Methods',
'woo-gutenberg-products-block'
) }
</Button>
</Placeholder>
);
};
export const Edit = ( {
attributes,
}: {
@ -62,17 +26,13 @@ export const Edit = ( {
} );
const { className } = attributes;
if ( ! isInitialized ) {
if ( ! isInitialized || ! hasExpressPaymentMethods ) {
return null;
}
return (
<div { ...blockProps }>
{ hasExpressPaymentMethods ? (
<Block className={ className } />
) : (
<NoExpressPaymentMethodsPlaceholder />
) }
<Block className={ className } />
</div>
);
};

View File

@ -21,7 +21,6 @@ import {
} from '@wordpress/components';
import { SlotFillProvider } from '@woocommerce/blocks-checkout';
import type { TemplateArray } from '@wordpress/blocks';
import { CartCheckoutFeedbackPrompt } from '@woocommerce/editor-components/feedback-prompt';
/**
* Internal dependencies
@ -134,7 +133,6 @@ export const Edit = ( {
/>
) }
</PanelBody>
<CartCheckoutFeedbackPrompt />
</InspectorControls>
);
const blockProps = useBlockPropsWithLocking();

View File

@ -8,7 +8,6 @@ import { PanelBody, ExternalLink } from '@wordpress/components';
import { ADMIN_URL } from '@woocommerce/settings';
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import Noninteractive from '@woocommerce/base-components/noninteractive';
import { CartCheckoutFeedbackPrompt } from '@woocommerce/editor-components/feedback-prompt';
/**
* Internal dependencies
@ -60,7 +59,6 @@ export const Edit = ( {
) }
</ExternalLink>
</PanelBody>
<CartCheckoutFeedbackPrompt />
</InspectorControls>
<Noninteractive>
<Block />

View File

@ -1,12 +1,8 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { Placeholder, Button } from '@wordpress/components';
import { useExpressPaymentMethods } from '@woocommerce/base-context/hooks';
import { Icon, payment } from '@wordpress/icons';
import { ADMIN_URL } from '@woocommerce/settings';
import classnames from 'classnames';
/**
@ -15,38 +11,6 @@ import classnames from 'classnames';
import Block from './block';
import './editor.scss';
/**
* Renders a placeholder in the editor.
*/
const NoExpressPaymentMethodsPlaceholder = () => {
return (
<Placeholder
icon={ <Icon icon={ payment } /> }
label={ __( 'Express Checkout', 'woo-gutenberg-products-block' ) }
className="wp-block-woocommerce-checkout-express-payment-block-placeholder"
>
<span className="wp-block-woocommerce-checkout-express-payment-block-placeholder__description">
{ __(
'Your store does not have any payment methods that support the Express Checkout block. Once you have configured a compatible payment method, it will be displayed here.',
'woo-gutenberg-products-block'
) }
</span>
<Button
isPrimary
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout` }
target="_blank"
rel="noopener noreferrer"
className="wp-block-woocommerce-checkout-express-payment-block-placeholder__button"
>
{ __(
'Configure Payment Methods',
'woo-gutenberg-products-block'
) }
</Button>
</Placeholder>
);
};
export const Edit = ( {
attributes,
}: {
@ -71,17 +35,13 @@ export const Edit = ( {
attributes,
} );
if ( ! isInitialized ) {
if ( ! isInitialized || ! hasExpressPaymentMethods ) {
return null;
}
return (
<div { ...blockProps }>
{ hasExpressPaymentMethods ? (
<Block />
) : (
<NoExpressPaymentMethodsPlaceholder />
) }
<Block />
</div>
);
};

View File

@ -8,10 +8,14 @@ import {
InspectorControls,
} from '@wordpress/block-editor';
import { CheckboxControl } from '@woocommerce/blocks-checkout';
import { PanelBody, ToggleControl, Notice } from '@wordpress/components';
import {
PanelBody,
ToggleControl,
Notice,
ExternalLink,
} from '@wordpress/components';
import { PRIVACY_URL, TERMS_URL } from '@woocommerce/block-settings';
import { ADMIN_URL } from '@woocommerce/settings';
import { Icon, external } from '@wordpress/icons';
/**
* Internal dependencies
@ -36,6 +40,82 @@ export const Edit = ( {
return (
<div { ...blockProps }>
<InspectorControls>
{ /* Show this notice if a terms page or a privacy page is not setup. */ }
{ ( ! TERMS_URL || ! PRIVACY_URL ) && (
<Notice
className="wc-block-checkout__terms_notice"
status="warning"
isDismissible={ false }
>
{ __(
"Link to your store's Terms and Conditions and Privacy Policy pages by creating pages for them.",
'woo-gutenberg-products-block'
) }
<br />
{ ! TERMS_URL && (
<>
<br />
<ExternalLink
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=advanced` }
>
{ __(
'Setup a Terms and Conditions page',
'woo-gutenberg-products-block'
) }
</ExternalLink>
</>
) }
{ ! PRIVACY_URL && (
<>
<br />
<ExternalLink
href={ `${ ADMIN_URL }options-privacy.php` }
>
{ __(
'Setup a Privacy Policy page',
'woo-gutenberg-products-block'
) }
</ExternalLink>
</>
) }
</Notice>
) }
{ /* Show this notice if we have both a terms and privacy pages, but they're not present in the text. */ }
{ TERMS_URL &&
PRIVACY_URL &&
! (
currentText.includes( TERMS_URL ) &&
currentText.includes( PRIVACY_URL )
) && (
<Notice
className="wc-block-checkout__terms_notice"
status="warning"
isDismissible={ false }
actions={
termsConsentDefaultText !== text
? [
{
label: __(
'Restore default text',
'woo-gutenberg-products-block'
),
onClick: () =>
setAttributes( {
text: '',
} ),
},
]
: []
}
>
<p>
{ __(
'Ensure you add links to your policy pages in this section.',
'woo-gutenberg-products-block'
) }
</p>
</Notice>
) }
<PanelBody
title={ __(
'Display options',
@ -80,100 +160,6 @@ export const Edit = ( {
/>
) }
</div>
{ /* Show this notice if a terms page or a privacy page is not setup. */ }
{ ( ! TERMS_URL || ! PRIVACY_URL ) && (
<Notice
className="wc-block-checkout__terms_notice"
status="warning"
isDismissible={ false }
actions={ [
! TERMS_URL && {
className: 'wc-block-checkout__terms_notice-button',
label: (
<>
{ __(
'Setup a Terms and Conditions page',
'woo-gutenberg-products-block'
) }
<Icon
icon={ external }
size={ 16 }
className="wc-block-checkout__terms_notice-button__icon"
/>
</>
),
onClick: () =>
window.open(
`${ ADMIN_URL }admin.php?page=wc-settings&tab=advanced`,
'_blank'
),
},
! PRIVACY_URL && {
className: 'wc-block-checkout__terms_notice-button',
label: (
<>
{ __(
'Setup a Privacy Policy page',
'woo-gutenberg-products-block'
) }
<Icon
size={ 16 }
icon={ external }
className="wc-block-checkout__terms_notice-button__icon"
/>
</>
),
onClick: () =>
window.open(
`${ ADMIN_URL }options-privacy.php`,
'_blank'
),
},
].filter( Boolean ) }
>
<p>
{ __(
"You don't have any Terms and Conditions and/or Privacy Policy pages set up.",
'woo-gutenberg-products-block'
) }
</p>
</Notice>
) }
{ /* Show this notice if we have both a terms and privacy pages, but they're not present in the text. */ }
{ TERMS_URL &&
PRIVACY_URL &&
! (
currentText.includes( TERMS_URL ) &&
currentText.includes( PRIVACY_URL )
) && (
<Notice
className="wc-block-checkout__terms_notice"
status="warning"
isDismissible={ false }
actions={
termsConsentDefaultText !== text
? [
{
label: __(
'Restore default text',
'woo-gutenberg-products-block'
),
onClick: () =>
setAttributes( { text: '' } ),
},
]
: []
}
>
<p>
{ __(
'Ensure you add links to your policy pages in this section.',
'woo-gutenberg-products-block'
) }
</p>
</Notice>
) }
</div>
);
};

View File

@ -20,20 +20,14 @@
}
}
.wc-block-checkout__terms_notice .components-notice__action {
margin-left: 0;
}
.wc-block-checkout__terms_notice-button {
display: flex;
flex-direction: row;
align-items: center;
.wc-block-checkout__terms_notice-button__icon {
margin-left: $gap-smallest;
.wc-block-checkout__terms_notice {
margin: 0;
.components-notice__content {
margin: 4px 0;
}
.components-notice__actions {
button {
margin-left: 0;
}
}
}
.wc-block-checkout__terms_notice .components-notice__content {
color: $black;
}

View File

@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { render, findByRole, queryByText } from '@testing-library/react';
import { render, queryByText } from '@testing-library/react';
/**
* Internal dependencies
@ -12,6 +12,7 @@ const blockSettingsMock = jest.requireMock( '@woocommerce/block-settings' );
jest.mock( '@wordpress/block-editor', () => ( {
...jest.requireActual( '@wordpress/block-editor' ),
useBlockProps: jest.fn(),
InspectorControls: jest.fn( ( { children } ) => <div>{ children }</div> ),
} ) );
jest.mock( '@woocommerce/block-settings', () => ( {
@ -32,7 +33,9 @@ describe( 'Edit', () => {
/>
);
expect( await findByRole( container, 'checkbox' ) ).toBeTruthy();
expect(
queryByText( container, 'I agree to the terms and conditions' )
).toBeTruthy();
} );
it( 'Renders a notice if either the terms and conditions or privacy url attribute are unset', async () => {
@ -59,7 +62,7 @@ describe( 'Edit', () => {
expect(
queryByText(
container,
"You don't have any Terms and Conditions and/or Privacy Policy pages set up."
"Link to your store's Terms and Conditions and Privacy Policy pages by creating pages for them."
)
).toBeInTheDocument();
} );

View File

@ -1,19 +1,14 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { __ } from '@wordpress/i18n';
import { store as editorStore } from '@wordpress/editor';
import triggerFetch from '@wordpress/api-fetch';
import { store as coreStore } from '@wordpress/core-data';
import { Notice, Button } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { CHECKOUT_PAGE_ID, CART_PAGE_ID } from '@woocommerce/block-settings';
import {
useCallback,
useState,
createInterpolateElement,
} from '@wordpress/element';
import { getAdminLink } from '@woocommerce/settings';
import { useCallback, useState } from '@wordpress/element';
/**
* Internal dependencies
*/
@ -156,36 +151,3 @@ export function DefaultNotice( { block }: { block: string } ) {
</Notice>
);
}
export function LegacyNotice( { block }: { block: string } ) {
return (
<Notice
className="wc-blocks-legacy-page-notice"
isDismissible={ false }
status="warning"
>
{ createInterpolateElement(
sprintf(
/* translators: %s is the block name. It will be cart or checkout. */
__(
'If you would like to use this block as your default %s you must update your <a>page settings in WooCommerce</a>.',
'woo-gutenberg-products-block'
),
block
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content
<a
href={ getAdminLink(
'admin.php?page=wc-settings&tab=advanced'
) }
target="_blank"
rel="noopener noreferrer"
/>
),
}
) }
</Notice>
);
}

View File

@ -0,0 +1,7 @@
.wc-blocks-no-payment-methods-notice {
margin: 0;
.components-notice__content {
margin: 4px 0;
}
}

View File

@ -0,0 +1,39 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Notice, ExternalLink } from '@wordpress/components';
import { ADMIN_URL } from '@woocommerce/settings';
/**
* Internal dependencies
*/
import './editor.scss';
export function NoPaymentMethodsNotice() {
const noticeContent = __(
'Your store does not have any payment methods that support the Checkout block. Once you have configured a compatible payment method it will be displayed here.',
'woo-gutenberg-products-block'
);
return (
<Notice
className="wc-blocks-no-payment-methods-notice"
status={ 'warning' }
spokenMessage={ noticeContent }
isDismissible={ false }
>
<div className="wc-blocks-no-payment-methods-notice__content">
{ noticeContent }{ ' ' }
<ExternalLink
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout` }
>
{ __(
'Configure Payment Methods',
'woo-gutenberg-products-block'
) }
</ExternalLink>
</div>
</Notice>
);
}