From 64ea78e913d4367b21c28f6b7a15fe09015b234f Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 12 Oct 2023 10:58:59 +0100 Subject: [PATCH 01/29] Add transforms for checkout block --- assets/js/blocks/checkout/index.tsx | 18 +++++ assets/js/blocks/classic-shortcode/block.json | 29 ++++++++ .../js/blocks/classic-shortcode/checkout.tsx | 2 +- .../js/blocks/classic-shortcode/constants.ts | 2 +- assets/js/blocks/classic-shortcode/index.tsx | 67 +++++++++---------- 5 files changed, 82 insertions(+), 36 deletions(-) create mode 100644 assets/js/blocks/classic-shortcode/block.json diff --git a/assets/js/blocks/checkout/index.tsx b/assets/js/blocks/checkout/index.tsx index e17c4f78931..7503bfb8b5e 100644 --- a/assets/js/blocks/checkout/index.tsx +++ b/assets/js/blocks/checkout/index.tsx @@ -31,6 +31,24 @@ const settings = { }, edit: Edit, save: Save, + transforms: { + to: [ + { + type: 'block', + blocks: [ 'woocommerce/classic-shortcode' ], + transform: ( attributes ) => { + return createBlock( + 'woocommerce/classic-shortcode', + { + shortcode: 'checkout', + align: attributes.align, + }, + [] + ); + }, + }, + ], + }, // Migrates v1 to v2 checkout. deprecated: [ { diff --git a/assets/js/blocks/classic-shortcode/block.json b/assets/js/blocks/classic-shortcode/block.json new file mode 100644 index 00000000000..59b15605270 --- /dev/null +++ b/assets/js/blocks/classic-shortcode/block.json @@ -0,0 +1,29 @@ +{ + "name": "woocommerce/classic-shortcode", + "version": "1.0.0", + "title": "Classic Shortcode", + "description": "Renders classic WooCommerce shortcodes.", + "category": "woocommerce", + "keywords": [ "WooCommerce" ], + "supports": { + "align": true, + "html": false, + "multiple": false, + "reusable": false, + "inserter": true + }, + "attributes": { + "shortcode": { + "type": "string", + "default": "cart", + "enum": [ "cart", "checkout" ] + }, + "align": { + "type": "string", + "default": "wide" + } + }, + "textdomain": "woo-gutenberg-products-block", + "apiVersion": 2, + "$schema": "https://schemas.wp.org/trunk/block.json" +} diff --git a/assets/js/blocks/classic-shortcode/checkout.tsx b/assets/js/blocks/classic-shortcode/checkout.tsx index 76f84a5a7b2..d0bccb15201 100644 --- a/assets/js/blocks/classic-shortcode/checkout.tsx +++ b/assets/js/blocks/classic-shortcode/checkout.tsx @@ -50,7 +50,7 @@ const onClickCallback = ( { }; const getTitle = () => { - return __( 'Checkout Shortcode', 'woo-gutenberg-products-block' ); + return __( 'Checkout Checkout', 'woo-gutenberg-products-block' ); }; const getDescription = () => { diff --git a/assets/js/blocks/classic-shortcode/constants.ts b/assets/js/blocks/classic-shortcode/constants.ts index e5707714a18..89d655e573e 100644 --- a/assets/js/blocks/classic-shortcode/constants.ts +++ b/assets/js/blocks/classic-shortcode/constants.ts @@ -31,7 +31,7 @@ export const TEMPLATES: TemplateDetails = { }, checkout: { type: TYPES.checkout, - title: __( 'Checkout Shortcode', 'woo-gutenberg-products-block' ), + title: __( 'Checkout Cart', 'woo-gutenberg-products-block' ), description: __( 'Renders the classic checkout shortcode.', 'woo-gutenberg-products-block' diff --git a/assets/js/blocks/classic-shortcode/index.tsx b/assets/js/blocks/classic-shortcode/index.tsx index ba960788158..49e05b60e51 100644 --- a/assets/js/blocks/classic-shortcode/index.tsx +++ b/assets/js/blocks/classic-shortcode/index.tsx @@ -20,7 +20,7 @@ import { ExternalLink, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { box, Icon } from '@wordpress/icons'; +import { shortcode, Icon } from '@wordpress/icons'; import { useDispatch, useSelect } from '@wordpress/data'; import { useState, createInterpolateElement } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; @@ -35,7 +35,7 @@ import { TEMPLATES, TYPES } from './constants'; import { getTemplateDetailsBySlug } from './utils'; import * as blockifiedCheckout from './checkout'; import * as blockifiedCart from './cart'; - +import metadata from './block.json'; import type { BlockifiedTemplateConfig } from './types'; type Attributes = { @@ -253,39 +253,13 @@ const Edit = ( { clientId, attributes }: BlockEditProps< Attributes > ) => { ); }; -registerBlockType( 'woocommerce/classic-shortcode', { - title: __( 'Classic Shortcode', 'woo-gutenberg-products-block' ), +const settings = { icon: ( - - ), - category: 'woocommerce', - apiVersion: 2, - keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ], - description: __( - 'Renders classic WooCommerce shortcodes.', - 'woo-gutenberg-products-block' + ), - - supports: { - align: true, - html: false, - multiple: false, - reusable: false, - inserter: false, - }, - attributes: { - /** - * Shortcode attribute is used to determine which shortcode gets rendered. - */ - shortcode: { - type: 'string', - default: 'any', - }, - align: { - type: 'string', - default: 'wide', - }, - }, edit: ( { attributes, clientId, @@ -300,4 +274,29 @@ registerBlockType( 'woocommerce/classic-shortcode', { ); }, save: () => null, -} ); + variations: [ + { + name: 'checkout', + title: __( 'Classic Checkout', 'woo-gutenberg-products-block' ), + attributes: { + shortcode: 'checkout', + }, + isActive: ( blockAttributes, variationAttributes ) => + blockAttributes.shortcode === variationAttributes.shortcode, + scope: [ 'inserter', 'transform' ], + }, + { + name: 'cart', + title: __( 'Classic Cart', 'woo-gutenberg-products-block' ), + attributes: { + shortcode: 'cart', + }, + isActive: ( blockAttributes, variationAttributes ) => + blockAttributes.shortcode === variationAttributes.shortcode, + scope: [ 'inserter', 'transform' ], + isDefault: true, + }, + ], +}; + +registerBlockType( metadata, settings ); From cbac3afc08c37f57c063e596fbbd372f29d799ce Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 12 Oct 2023 14:53:38 +0100 Subject: [PATCH 02/29] Block to shortcode switcher in notice --- .../sidebar-notices/index.tsx | 83 +++++++------ .../incompatible-extension-notice/index.tsx | 110 ++++++++++++++++-- .../incompatible-extension-notice/modal.tsx | 66 +++++++++++ 3 files changed, 217 insertions(+), 42 deletions(-) create mode 100644 assets/js/editor-components/incompatible-extension-notice/modal.tsx diff --git a/assets/js/blocks/cart-checkout-shared/sidebar-notices/index.tsx b/assets/js/blocks/cart-checkout-shared/sidebar-notices/index.tsx index f18826c114e..be6f7b8196e 100644 --- a/assets/js/blocks/cart-checkout-shared/sidebar-notices/index.tsx +++ b/assets/js/blocks/cart-checkout-shared/sidebar-notices/index.tsx @@ -48,36 +48,54 @@ const withSidebarNotices = createHigherOrderComponent( setIsIncompatibleExtensionsNoticeDismissed( isDismissed ); }; - const { isCart, isCheckout, isPaymentMethodsBlock, hasPaymentMethods } = - useSelect( ( select ) => { - const { getBlockParentsByBlockName, getBlockName } = - select( blockEditorStore ); - const parent = getBlockParentsByBlockName( clientId, [ - 'woocommerce/cart', - 'woocommerce/checkout', - ] ).map( getBlockName ); - const currentBlockName = getBlockName( clientId ); - return { - isCart: - parent.includes( 'woocommerce/cart' ) || - currentBlockName === 'woocommerce/cart', - isCheckout: - parent.includes( 'woocommerce/checkout' ) || - currentBlockName === 'woocommerce/checkout', - isPaymentMethodsBlock: - currentBlockName === - 'woocommerce/checkout-payment-block', - hasPaymentMethods: - select( - PAYMENT_STORE_KEY - ).paymentMethodsInitialized() && - Object.keys( - select( - PAYMENT_STORE_KEY - ).getAvailablePaymentMethods() - ).length > 0, - }; - } ); + const { + isCart, + isCheckout, + isPaymentMethodsBlock, + hasPaymentMethods, + parentId, + } = useSelect( ( select ) => { + const { getBlockParentsByBlockName, getBlockName } = + select( blockEditorStore ); + + const parents = getBlockParentsByBlockName( clientId, [ + 'woocommerce/cart', + 'woocommerce/checkout', + ] ).reduce( + ( + accumulator: Record< string, string >, + parentClientId: string + ) => { + const parentName = getBlockName( parentClientId ); + accumulator[ parentName ] = parentClientId; + return accumulator; + }, + {} + ); + + const currentBlockName = getBlockName( clientId ); + const currentBlockIsCart = + Object.keys( parents ).includes( 'woocommerce/cart' ) || + currentBlockName === 'woocommerce/cart'; + const currentBlockIsCheckout = + Object.keys( parents ).includes( 'woocommerce/checkout' ) || + currentBlockName === 'woocommerce/checkout'; + + return { + isCart: currentBlockIsCart, + isCheckout: currentBlockIsCheckout, + parentId: currentBlockIsCart + ? parents[ 'woocommerce/cart' ] + : parents[ 'woocommerce/checkout' ], + isPaymentMethodsBlock: + currentBlockName === 'woocommerce/checkout-payment-block', + hasPaymentMethods: + select( PAYMENT_STORE_KEY ).paymentMethodsInitialized() && + Object.keys( + select( PAYMENT_STORE_KEY ).getAvailablePaymentMethods() + ).length > 0, + }; + } ); // Show sidebar notices only when a WooCommerce block is selected. if ( @@ -96,10 +114,9 @@ const withSidebarNotices = createHigherOrderComponent( toggleIncompatibleExtensionsNoticeDismissedStatus } block={ - isCheckout - ? 'woocommerce/checkout' - : 'woocommerce/cart' + isCart ? 'woocommerce/cart' : 'woocommerce/checkout' } + clientId={ parentId } /> diff --git a/assets/js/editor-components/incompatible-extension-notice/index.tsx b/assets/js/editor-components/incompatible-extension-notice/index.tsx index bddec61bd86..3e41b2e5d13 100644 --- a/assets/js/editor-components/incompatible-extension-notice/index.tsx +++ b/assets/js/editor-components/incompatible-extension-notice/index.tsx @@ -2,24 +2,34 @@ * External dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { Notice, ExternalLink } from '@wordpress/components'; -import { createInterpolateElement, useEffect } from '@wordpress/element'; +import { Notice, ExternalLink, Button } from '@wordpress/components'; +import { + createInterpolateElement, + useEffect, + useState, +} from '@wordpress/element'; import { Alert } from '@woocommerce/icons'; import { Icon } from '@wordpress/icons'; +import { useDispatch } from '@wordpress/data'; +import { createBlock } from '@wordpress/blocks'; + /** * Internal dependencies */ import { useCombinedIncompatibilityNotice } from './use-combined-incompatibility-notice'; +import { Modal, ModalFooter } from './modal'; import './editor.scss'; interface ExtensionNoticeProps { toggleDismissedStatus: ( status: boolean ) => void; block: 'woocommerce/cart' | 'woocommerce/checkout'; + clientId: string; } export function IncompatibleExtensionsNotice( { toggleDismissedStatus, block, + clientId, }: ExtensionNoticeProps ) { const [ isVisible, @@ -27,6 +37,10 @@ export function IncompatibleExtensionsNotice( { incompatiblePaymentMethods, numberOfIncompatiblePaymentMethods, ] = useCombinedIncompatibilityNotice( block ); + const [ isOpen, setOpen ] = useState( false ); + const openModal = () => setOpen( true ); + const closeModal = () => setOpen( false ); + const { replaceBlock } = useDispatch( 'core/block-editor' ); useEffect( () => { toggleDismissedStatus( ! isVisible ); @@ -36,15 +50,26 @@ export function IncompatibleExtensionsNotice( { return null; } - // console.log( incompatiblePaymentMethods ); + const switchButtonLabel = + block === 'woocommerce/cart' + ? __( 'Switch to classic cart', 'woo-gutenberg-products-block' ) + : __( + 'Switch to classic checkout', + 'woo-gutenberg-products-block' + ); + const blockLabel = block === 'woocommerce/cart' ? 'cart' : 'checkout'; const noticeContent = ( <> { numberOfIncompatiblePaymentMethods > 1 ? createInterpolateElement( - __( - 'The following extensions may be incompatible with the block-based checkout. Learn more', - 'woo-gutenberg-products-block' + sprintf( + // translators: %s is the name of the parent block. + __( + 'The following extensions may be incompatible with the block-based %s. Learn more', + 'woo-gutenberg-products-block' + ), + blockLabel ), { a: ( @@ -54,12 +79,13 @@ export function IncompatibleExtensionsNotice( { ) : createInterpolateElement( sprintf( - // translators: %s is the name of the extension that is incompatible with the block-based checkout. + // translators: %1$s is the name of the extension, %2$s is the name of the parent block. __( - '%s may be incompatible with the block-based checkout. Learn more', + '%1$s may be incompatible with the block-based %2$s. Learn more', 'woo-gutenberg-products-block' ), - Object.values( incompatiblePaymentMethods )[ 0 ] + Object.values( incompatiblePaymentMethods )[ 0 ], + blockLabel ), { strong: , @@ -99,6 +125,72 @@ export function IncompatibleExtensionsNotice( { ) } ) } + + { isOpen && ( + +

+ { sprintf( + // translators: %s is the name of the parent block. + __( + 'If you turn off the new %1$s it will be replaced with the classic %1$s shortcode. This means you may lose:', + 'woo-gutenberg-products-block' + ), + blockLabel + ) } +

+
    +
  • + { __( + 'Customizations and updates to the block', + 'woo-gutenberg-products-block' + ) } +
  • +
  • + { __( + 'Additional local pickup options created for the new cart/checkout', + 'woo-gutenberg-products-block' + ) } +
  • +
+ + { ' ' } + + +
+ ) } diff --git a/assets/js/editor-components/incompatible-extension-notice/modal.tsx b/assets/js/editor-components/incompatible-extension-notice/modal.tsx new file mode 100644 index 00000000000..9a5d60d7f97 --- /dev/null +++ b/assets/js/editor-components/incompatible-extension-notice/modal.tsx @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +import styled from '@emotion/styled'; +import { Modal as ModalComponent } from '@wordpress/components'; + +export const Modal = styled( ModalComponent )` + max-width: 600px; + border-radius: 4px; + @media ( min-width: 600px ) { + min-width: 560px; + } + + .components-modal__header { + padding: 12px 24px; + border-bottom: 1px solid #e0e0e0; + position: relative; + height: auto; + width: auto; + margin: 0 -24px 16px; + + @media ( max-width: 599px ) { + button { + display: none; + } + } + } + + .components-modal__content { + margin: 0; + padding: 0 24px; + + @media ( max-width: 599px ) { + display: flex; + flex-direction: column; + + hr:last-of-type { + margin-top: auto; + } + } + + ul { + list-style: disc inside; + margin: 0 0 24px; + } + } +`; + +export const ModalFooter = styled.div` + display: flex; + justify-content: flex-end; + border-top: 1px solid #e0e0e0; + margin: 24px -24px 0; + padding: 24px; + + > * { + &:not( :first-of-type ) { + margin-left: 8px; + } + } + + .button-link-delete { + margin-right: auto; + color: #d63638; + } +`; From 2256e979ff9d9a4ba774c3d76b1a854d831e69cc Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 12 Oct 2023 14:55:34 +0100 Subject: [PATCH 03/29] cart transforms --- assets/js/blocks/cart/index.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/assets/js/blocks/cart/index.js b/assets/js/blocks/cart/index.js index b172a86b1bb..b9f2caa8324 100644 --- a/assets/js/blocks/cart/index.js +++ b/assets/js/blocks/cart/index.js @@ -45,6 +45,24 @@ const settings = { attributes: blockAttributes, edit: Edit, save: Save, + transforms: { + to: [ + { + type: 'block', + blocks: [ 'woocommerce/classic-shortcode' ], + transform: ( attributes ) => { + return createBlock( + 'woocommerce/classic-shortcode', + { + shortcode: 'cart', + align: attributes.align, + }, + [] + ); + }, + }, + ], + }, // Migrates v1 to v2 checkout. deprecated: [ { From 07b9a1c319e5a75b56b86d4078b3da32775a86c2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 16 Oct 2023 12:41:10 +0100 Subject: [PATCH 04/29] Fix target block for switching --- .../sidebar-notices/index.tsx | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/assets/js/blocks/cart-checkout-shared/sidebar-notices/index.tsx b/assets/js/blocks/cart-checkout-shared/sidebar-notices/index.tsx index be6f7b8196e..0db88d11725 100644 --- a/assets/js/blocks/cart-checkout-shared/sidebar-notices/index.tsx +++ b/assets/js/blocks/cart-checkout-shared/sidebar-notices/index.tsx @@ -74,19 +74,27 @@ const withSidebarNotices = createHigherOrderComponent( ); const currentBlockName = getBlockName( clientId ); + const parentBlockIsCart = + Object.keys( parents ).includes( 'woocommerce/cart' ); + const parentBlockIsCheckout = Object.keys( parents ).includes( + 'woocommerce/checkout' + ); const currentBlockIsCart = - Object.keys( parents ).includes( 'woocommerce/cart' ) || - currentBlockName === 'woocommerce/cart'; + currentBlockName === 'woocommerce/cart' || parentBlockIsCart; const currentBlockIsCheckout = - Object.keys( parents ).includes( 'woocommerce/checkout' ) || - currentBlockName === 'woocommerce/checkout'; + currentBlockName === 'woocommerce/checkout' || + parentBlockIsCheckout; + const targetParentBlock = currentBlockIsCart + ? 'woocommerce/cart' + : 'woocommerce/checkout'; return { isCart: currentBlockIsCart, isCheckout: currentBlockIsCheckout, - parentId: currentBlockIsCart - ? parents[ 'woocommerce/cart' ] - : parents[ 'woocommerce/checkout' ], + parentId: + currentBlockName === targetParentBlock + ? clientId + : parents[ targetParentBlock ], isPaymentMethodsBlock: currentBlockName === 'woocommerce/checkout-payment-block', hasPaymentMethods: From ac821fb86e58dc461125e2520a37d7e37123ad04 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 16 Oct 2023 13:31:40 +0100 Subject: [PATCH 05/29] Remove switcher UI for classic cart/checkout --- assets/js/blocks/classic-shortcode/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/js/blocks/classic-shortcode/index.tsx b/assets/js/blocks/classic-shortcode/index.tsx index 49e05b60e51..38e65aa96b9 100644 --- a/assets/js/blocks/classic-shortcode/index.tsx +++ b/assets/js/blocks/classic-shortcode/index.tsx @@ -283,7 +283,7 @@ const settings = { }, isActive: ( blockAttributes, variationAttributes ) => blockAttributes.shortcode === variationAttributes.shortcode, - scope: [ 'inserter', 'transform' ], + scope: [ 'inserter' ], }, { name: 'cart', @@ -293,7 +293,7 @@ const settings = { }, isActive: ( blockAttributes, variationAttributes ) => blockAttributes.shortcode === variationAttributes.shortcode, - scope: [ 'inserter', 'transform' ], + scope: [ 'inserter' ], isDefault: true, }, ], From 42735bf77813bea18eeb6732fffb6a6038c20a76 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 16 Oct 2023 13:40:07 +0100 Subject: [PATCH 06/29] Set isPreview when generating block preview in switcher --- assets/js/blocks/classic-shortcode/index.tsx | 5 ++++- assets/js/blocks/classic-template/index.tsx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/assets/js/blocks/classic-shortcode/index.tsx b/assets/js/blocks/classic-shortcode/index.tsx index 38e65aa96b9..d9daca4a73f 100644 --- a/assets/js/blocks/classic-shortcode/index.tsx +++ b/assets/js/blocks/classic-shortcode/index.tsx @@ -156,7 +156,10 @@ const ConvertTemplate = ( { blockifyConfig, clientId, attributes } ) => { } } > { } } > Date: Mon, 16 Oct 2023 15:46:27 +0100 Subject: [PATCH 07/29] Onboarding task --- .../incompatible-extension-notice/index.tsx | 4 +- src/Domain/Bootstrap.php | 9 ++ .../OnboardingTasks/ReviewCheckoutTask.php | 86 +++++++++++++++++++ .../OnboardingTasks/TasksController.php | 29 +++++++ 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php create mode 100644 src/Domain/Services/OnboardingTasks/TasksController.php diff --git a/assets/js/editor-components/incompatible-extension-notice/index.tsx b/assets/js/editor-components/incompatible-extension-notice/index.tsx index 3e41b2e5d13..18a0e40f6e4 100644 --- a/assets/js/editor-components/incompatible-extension-notice/index.tsx +++ b/assets/js/editor-components/incompatible-extension-notice/index.tsx @@ -66,7 +66,7 @@ export function IncompatibleExtensionsNotice( { sprintf( // translators: %s is the name of the parent block. __( - 'The following extensions may be incompatible with the block-based %s. Learn more', + 'Some extensions do not yet support the new %s and may impact the shopper experience. Learn more', 'woo-gutenberg-products-block' ), blockLabel @@ -81,7 +81,7 @@ export function IncompatibleExtensionsNotice( { sprintf( // translators: %1$s is the name of the extension, %2$s is the name of the parent block. __( - '%1$s may be incompatible with the block-based %2$s. Learn more', + '%1$s does not yet support the new %2$s and may impact the shopper experience. Learn more', 'woo-gutenberg-products-block' ), Object.values( incompatiblePaymentMethods )[ 0 ], diff --git a/src/Domain/Bootstrap.php b/src/Domain/Bootstrap.php index 9448126ab1a..301a93a9ea7 100644 --- a/src/Domain/Bootstrap.php +++ b/src/Domain/Bootstrap.php @@ -36,6 +36,7 @@ use Automattic\WooCommerce\Blocks\Shipping\ShippingController; use Automattic\WooCommerce\Blocks\Templates\SingleProductTemplateCompatibility; use Automattic\WooCommerce\Blocks\Templates\ArchiveProductTemplatesCompatibility; +use Automattic\WooCommerce\Blocks\Domain\Services\OnboardingTasks\TasksController; /** * Takes care of bootstrapping the plugin. @@ -129,6 +130,7 @@ function() { $this->container->get( DraftOrders::class )->init(); $this->container->get( CreateAccount::class )->init(); $this->container->get( ShippingController::class )->init(); + $this->container->get( TasksController::class )->init(); // Load assets in admin and on the frontend. if ( ! $is_rest ) { @@ -137,6 +139,7 @@ function() { $this->container->get( AssetsController::class ); $this->container->get( Installer::class )->init(); $this->container->get( GoogleAnalytics::class )->init(); + } // Load assets unless this is a request specifically for the store API. @@ -431,6 +434,12 @@ function ( $container ) { return new ShippingController( $asset_api, $asset_data_registry ); } ); + $this->container->register( + TasksController::class, + function() { + return new TasksController(); + } + ); } /** diff --git a/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php b/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php new file mode 100644 index 00000000000..5dbaff95929 --- /dev/null +++ b/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php @@ -0,0 +1,86 @@ + 0; + } + + /** + * Action URL. + * + * @return string + */ + public function get_action_url() { + $checkout_page_id = wc_get_page_id( 'checkout' ); + + return admin_url( 'site-editor.php?postType=page&postId=' . $checkout_page_id ); + } +} diff --git a/src/Domain/Services/OnboardingTasks/TasksController.php b/src/Domain/Services/OnboardingTasks/TasksController.php new file mode 100644 index 00000000000..2e1258b2bcc --- /dev/null +++ b/src/Domain/Services/OnboardingTasks/TasksController.php @@ -0,0 +1,29 @@ + Date: Mon, 16 Oct 2023 16:12:24 +0100 Subject: [PATCH 08/29] Action on click --- .../OnboardingTasks/ReviewCheckoutTask.php | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php b/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php index 5dbaff95929..8c162a2f2ec 100644 --- a/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php +++ b/src/Domain/Services/OnboardingTasks/ReviewCheckoutTask.php @@ -23,7 +23,7 @@ public function get_id() { * @return string */ public function get_title() { - return __( 'Review the shopper checkout experience', 'woo-gutenberg-products-block' ); + return __( 'Review your shopper\'s checkout experience', 'woo-gutenberg-products-block' ); } /** @@ -32,7 +32,7 @@ public function get_title() { * @return string */ public function get_content() { - return __( 'Make sure cart and checkout flows are configured correctly for your shoppers.', 'woo-gutenberg-products-block' ); + return ''; } /** @@ -45,12 +45,12 @@ public function get_time() { } /** - * Check if a task is dismissable. + * Additional Info. * - * @return bool + * @return string */ - public function is_dismissable() { - return false; + public function get_additional_info() { + return __( 'Make sure cart and checkout flows are configured correctly for your shoppers.', 'woo-gutenberg-products-block' ); } /** @@ -59,7 +59,40 @@ public function is_dismissable() { * @return bool */ public function is_complete() { - return false; + return $this->is_visited(); + } + + /** + * Check if the store uses blocks on the cart or checkout page. + * + * @return boolean + */ + private function has_cart_block() { + $cart_page_id = wc_get_page_id( 'cart' ); + $has_block_cart = $cart_page_id && has_block( 'woocommerce/cart', $cart_page_id ); + + return $has_block_cart; + } + + /** + * Check if the store uses blocks on the cart or checkout page. + * + * @return boolean + */ + private function has_checkout_block() { + $cart_page_id = wc_get_page_id( 'cart' ); + $has_block_cart = $cart_page_id && has_block( 'woocommerce/cart', $cart_page_id ); + + return $has_block_cart; + } + + /** + * Check if the store uses blocks on the cart or checkout page. + * + * @return boolean + */ + private function has_cart_or_checkout_block() { + return $this->has_cart_block() || $this->has_checkout_block(); } /** @@ -68,9 +101,7 @@ public function is_complete() { * @return bool */ public function can_view() { - $checkout_page_id = wc_get_page_id( 'checkout' ); - - return $checkout_page_id > 0; + return $this->has_cart_or_checkout_block(); } /** @@ -79,8 +110,8 @@ public function can_view() { * @return string */ public function get_action_url() { - $checkout_page_id = wc_get_page_id( 'checkout' ); + $page_id = $this->has_cart_block() ? wc_get_page_id( 'cart' ) : wc_get_page_id( 'checkout' ); - return admin_url( 'site-editor.php?postType=page&postId=' . $checkout_page_id ); + return admin_url( 'site-editor.php?postType=page&postId=' . $page_id ); } } From b3769e912e4de2ecf4ada376b730db69c5c371b2 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 16 Oct 2023 16:20:33 +0100 Subject: [PATCH 09/29] Focus on block after replacement --- .../incompatible-extension-notice/index.tsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/assets/js/editor-components/incompatible-extension-notice/index.tsx b/assets/js/editor-components/incompatible-extension-notice/index.tsx index 18a0e40f6e4..784c007f944 100644 --- a/assets/js/editor-components/incompatible-extension-notice/index.tsx +++ b/assets/js/editor-components/incompatible-extension-notice/index.tsx @@ -10,7 +10,7 @@ import { } from '@wordpress/element'; import { Alert } from '@woocommerce/icons'; import { Icon } from '@wordpress/icons'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { createBlock } from '@wordpress/blocks'; /** @@ -41,6 +41,11 @@ export function IncompatibleExtensionsNotice( { const openModal = () => setOpen( true ); const closeModal = () => setOpen( false ); const { replaceBlock } = useDispatch( 'core/block-editor' ); + const { getBlocks, selectBlock } = useSelect( ( select ) => { + return { + getBlocks: select( 'core/block-editor' ).getBlocks, + }; + }, [] ); useEffect( () => { toggleDismissedStatus( ! isVisible ); @@ -174,6 +179,19 @@ export function IncompatibleExtensionsNotice( { } ) ); + const blocks = getBlocks(); + + const shortcodeBlock = blocks.find( + ( foundBlock ) => + foundBlock.name === + 'woocommerce/classic-shortcode' + ); + + if ( shortcodeBlock ) { + selectBlock( + shortcodeBlock.clientId + ); + } closeModal(); } } > From 35393c16f2ec857b6b5a3bc4f0f676585d5a4711 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Mon, 16 Oct 2023 17:06:25 +0100 Subject: [PATCH 10/29] Update notice styling and wording --- .../incompatible-extension-notice/editor.scss | 15 ++++----------- .../incompatible-extension-notice/index.tsx | 3 ++- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/assets/js/editor-components/incompatible-extension-notice/editor.scss b/assets/js/editor-components/incompatible-extension-notice/editor.scss index 5a10598ea7f..b4e4bad1415 100644 --- a/assets/js/editor-components/incompatible-extension-notice/editor.scss +++ b/assets/js/editor-components/incompatible-extension-notice/editor.scss @@ -21,17 +21,10 @@ min-width: max-content; } } - .wc-blocks-incompatible-extensions-notice__element { - display: flex; - align-items: center; - position: relative; - - &::before { - content: "•"; - position: absolute; - left: -13px; - font-size: 1.2rem; - } + ul { + margin: 0 0 1em 1.5em; + padding: 0; + list-style: disc outside; } } diff --git a/assets/js/editor-components/incompatible-extension-notice/index.tsx b/assets/js/editor-components/incompatible-extension-notice/index.tsx index 784c007f944..2ed1db903b9 100644 --- a/assets/js/editor-components/incompatible-extension-notice/index.tsx +++ b/assets/js/editor-components/incompatible-extension-notice/index.tsx @@ -142,7 +142,7 @@ export function IncompatibleExtensionsNotice( { { sprintf( // translators: %s is the name of the parent block. __( - 'If you turn off the new %1$s it will be replaced with the classic %1$s shortcode. This means you may lose:', + 'If you continue, the %1$s block will be replaced with the classic %1$s shortcode. This means you may lose:', 'woo-gutenberg-products-block' ), blockLabel @@ -165,6 +165,7 @@ export function IncompatibleExtensionsNotice( { { isOpen && ( @@ -148,19 +176,21 @@ export function IncompatibleExtensionsNotice( { blockLabel ) }

-
    +
    • { __( 'Customizations and updates to the block', 'woo-gutenberg-products-block' ) }
    • -
    • - { __( - 'Additional local pickup options created for the new cart/checkout', - 'woo-gutenberg-products-block' - ) } -
    • + { block === 'woocommerce/checkout' && ( +
    • + { __( + 'Additional local pickup options created for the new cart/checkout', + 'woo-gutenberg-products-block' + ) } +
    • + ) }
) } - { isOpen && ( @@ -179,6 +200,16 @@ export function IncompatibleExtensionsNotice( { } ) ); + recordEvent( + 'switch_to_classic_shortcode_confirm', + { + shortcode: + block === + 'woocommerce/checkout' + ? 'checkout' + : 'cart', + } + ); selectClassicShortcodeBlock(); createInfoNotice( __( @@ -194,6 +225,16 @@ export function IncompatibleExtensionsNotice( { ), onClick: () => { undo(); + recordEvent( + 'switch_to_classic_shortcode_undo', + { + shortcode: + block === + 'woocommerce/checkout' + ? 'checkout' + : 'cart', + } + ); }, }, ], @@ -207,7 +248,19 @@ export function IncompatibleExtensionsNotice( { { ' ' } - + ); }; - const Edit = ( { clientId, attributes }: BlockEditProps< Attributes > ) => { const blockProps = useBlockProps(); From a43da7dc6cf6628bbd6de661900acf9e83845dc7 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Wed, 18 Oct 2023 15:50:54 +0100 Subject: [PATCH 23/29] Add align to wrapper --- src/BlockTypes/ClassicShortcode.php | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/BlockTypes/ClassicShortcode.php b/src/BlockTypes/ClassicShortcode.php index b2bcd7d33d6..0db6af24f25 100644 --- a/src/BlockTypes/ClassicShortcode.php +++ b/src/BlockTypes/ClassicShortcode.php @@ -50,29 +50,46 @@ protected function render( $attributes, $content, $block ) { } if ( 'cart' === $attributes['shortcode'] ) { - return $this->render_cart(); + return $this->render_cart( $attributes ); } if ( 'checkout' === $attributes['shortcode'] ) { - return $this->render_checkout(); + return $this->render_checkout( $attributes ); } return "You're using the ClassicShortcode block"; } + /** + * Get the list of classes to apply to this block. + * + * @param array $attributes Block attributes. Default empty array. + * @return string space-separated list of classes. + */ + protected function get_container_classes( $attributes = array() ) { + $classes = array( 'wp-block-group' ); + + if ( isset( $attributes['align'] ) ) { + $classes[] = "align{$attributes['align']}"; + } + + return implode( ' ', $classes ); + } + /** * Render method for rendering the cart shortcode. * + * @param array $attributes Block attributes. * @return string Rendered block type output. */ - protected function render_cart() { + protected function render_cart( $attributes ) { if ( ! isset( WC()->cart ) ) { return ''; } ob_start(); - echo '
'; + echo '
'; WC_Shortcode_Cart::output( array() ); echo '
'; @@ -82,16 +99,17 @@ protected function render_cart() { /** * Render method for rendering the checkout shortcode. * + * @param array $attributes Block attributes. * @return string Rendered block type output. */ - protected function render_checkout() { + protected function render_checkout( $attributes ) { if ( ! isset( WC()->cart ) ) { return ''; } ob_start(); - echo '
'; + echo '
'; WC_Shortcode_Checkout::output( array() ); echo '
'; From 7038b0cff65c927d7d9ad5a6e7fe707de1d05bbe Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 19 Oct 2023 13:44:31 +0100 Subject: [PATCH 24/29] Update modal content --- .../incompatible-extension-notice/index.tsx | 9 +++-- .../incompatible-extension-notice/modal.tsx | 40 ++++++++++--------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/assets/js/editor-components/incompatible-extension-notice/index.tsx b/assets/js/editor-components/incompatible-extension-notice/index.tsx index b43da597e38..4fb1f78b7c1 100644 --- a/assets/js/editor-components/incompatible-extension-notice/index.tsx +++ b/assets/js/editor-components/incompatible-extension-notice/index.tsx @@ -84,7 +84,7 @@ export function IncompatibleExtensionsNotice( { { numberOfIncompatiblePaymentMethods > 1 ? createInterpolateElement( __( - 'Some extensions do not yet support the new cart and checkout blocks. This may impact the shopper experience. Learn more', + 'Some active extensions do not yet support this block. This may impact the shopper experience. Learn more', 'woo-gutenberg-products-block' ), { @@ -97,7 +97,7 @@ export function IncompatibleExtensionsNotice( { sprintf( // translators: %s is the name of the extension. __( - '%s does not yet support the new cart and checkout blocks. This may impact the shopper experience. Learn more', + '%s does not yet support this block. This may impact the shopper experience. Learn more', 'woo-gutenberg-products-block' ), Object.values( incompatiblePaymentMethods )[ 0 ] @@ -234,7 +234,10 @@ export function IncompatibleExtensionsNotice( { closeModal(); } } > - { switchButtonLabel } + { __( + 'Switch', + 'woo-gutenberg-products-block' + ) } { ' ' } { isOpen && ( - + - + ) }
diff --git a/assets/js/editor-components/incompatible-extension-notice/modal.tsx b/assets/js/editor-components/incompatible-extension-notice/modal.tsx index f2a41746bc0..d8c528cda7b 100644 --- a/assets/js/editor-components/incompatible-extension-notice/modal.tsx +++ b/assets/js/editor-components/incompatible-extension-notice/modal.tsx @@ -2,50 +2,6 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import styled from '@emotion/styled'; -import { Modal as ModalComponent } from '@wordpress/components'; - -export const Modal = styled( ModalComponent )` - max-width: 600px; - border-radius: 4px; - @media ( min-width: 600px ) { - min-width: 560px; - } - - .components-modal__header { - padding: 12px 24px; - border-bottom: 1px solid #e0e0e0; - position: relative; - height: auto; - width: auto; - margin: 0 -24px 16px; - - @media ( max-width: 599px ) { - button { - display: none; - } - } - } - - .components-modal__content { - margin: 0; - padding: 0 24px; - - @media ( max-width: 599px ) { - display: flex; - flex-direction: column; - - hr:last-of-type { - margin-top: auto; - } - } - - ul { - list-style: disc inside; - margin: 0 0 24px; - } - } -`; export const ModalContent = ( { blockType = 'woocommerce/cart', @@ -88,22 +44,3 @@ export const ModalContent = ( { ); }; - -export const ModalFooter = styled.div` - display: flex; - justify-content: flex-end; - border-top: 1px solid #e0e0e0; - margin: 24px -24px 0; - padding: 24px; - - > * { - &:not( :first-of-type ) { - margin-left: 8px; - } - } - - .button-link-delete { - margin-right: auto; - color: #d63638; - } -`; From 2babbab4c1e69861a0371ff745e85d80ff6bbab1 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 19 Oct 2023 14:40:37 +0100 Subject: [PATCH 26/29] Removed undo link when converting from classic shortcode --- assets/js/blocks/classic-shortcode/index.tsx | 43 +------------------- 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/assets/js/blocks/classic-shortcode/index.tsx b/assets/js/blocks/classic-shortcode/index.tsx index 2ffe5dd8d64..29a03b6b5b2 100644 --- a/assets/js/blocks/classic-shortcode/index.tsx +++ b/assets/js/blocks/classic-shortcode/index.tsx @@ -1,11 +1,7 @@ /** * External dependencies */ -import { - BlockInstance, - createBlock, - registerBlockType, -} from '@wordpress/blocks'; +import { registerBlockType } from '@wordpress/blocks'; import type { BlockEditProps } from '@wordpress/blocks'; import { useBlockProps, @@ -25,7 +21,6 @@ import { useDispatch, useSelect } from '@wordpress/data'; import { useState, createInterpolateElement } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { woo } from '@woocommerce/icons'; -import { findBlock } from '@woocommerce/utils'; /** * Internal dependencies @@ -91,42 +86,6 @@ const ConvertTemplate = ( { blockifyConfig, clientId, attributes } ) => { 'woo-gutenberg-products-block' ), { - actions: [ - { - label: __( - 'Undo', - 'woo-gutenberg-products-block' - ), - onClick: () => { - const targetBlocks = [ - 'woocommerce/cart', - 'woocommerce/checkout', - ]; - const cartCheckoutBlock = findBlock( { - blocks: getBlocks(), - findCondition: ( - foundBlock: BlockInstance - ) => - targetBlocks.includes( - foundBlock.name - ), - } ); - if ( ! cartCheckoutBlock ) { - return; - } - replaceBlock( - cartCheckoutBlock.clientId, - createBlock( - 'woocommerce/classic-shortcode', - { - shortcode: - attributes.shortcode, - } - ) - ); - }, - }, - ], type: 'snackbar', } ); From fd5f1c116a8ea3d814b154230528b56e8c09c907 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 19 Oct 2023 15:40:54 +0100 Subject: [PATCH 27/29] Check if block was selected --- assets/js/blocks/cart/edit.js | 13 ++++++++----- assets/js/blocks/checkout/edit.tsx | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/assets/js/blocks/cart/edit.js b/assets/js/blocks/cart/edit.js index 4b438c7d44b..1df376294d1 100644 --- a/assets/js/blocks/cart/edit.js +++ b/assets/js/blocks/cart/edit.js @@ -13,9 +13,9 @@ import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundar import { EditorProvider, CartProvider } from '@woocommerce/base-context'; import { previewCart } from '@woocommerce/resource-previews'; import { SlotFillProvider } from '@woocommerce/blocks-checkout'; -import { useEffect } from '@wordpress/element'; +import { useEffect, useRef } from '@wordpress/element'; import { getQueryArg } from '@wordpress/url'; -import { dispatch } from '@wordpress/data'; +import { dispatch, select } from '@wordpress/data'; /** * Internal dependencies @@ -53,17 +53,20 @@ export const Edit = ( { clientId, className, attributes, setAttributes } ) => { } ); // This focuses on the block when a certain query param is found. This is used on the link from the task list. - const focus = getQueryArg( window.location.href, 'focus' ); + const focus = useRef( getQueryArg( window.location.href, 'focus' ) ); useEffect( () => { - if ( focus === 'cart' ) { + if ( + focus.current === 'cart' && + ! select( 'core/block-editor' ).hasSelectedBlock() + ) { dispatch( 'core/block-editor' ).selectBlock( clientId ); dispatch( 'core/interface' ).enableComplementaryArea( 'core/edit-site', 'edit-site/block-inspector' ); } - }, [ clientId, focus ] ); + }, [ clientId ] ); return (
diff --git a/assets/js/blocks/checkout/edit.tsx b/assets/js/blocks/checkout/edit.tsx index 4ab42ed4a5d..1a41ec78772 100644 --- a/assets/js/blocks/checkout/edit.tsx +++ b/assets/js/blocks/checkout/edit.tsx @@ -21,9 +21,9 @@ import { } from '@wordpress/components'; import { SlotFillProvider } from '@woocommerce/blocks-checkout'; import type { TemplateArray } from '@wordpress/blocks'; -import { useEffect } from '@wordpress/element'; +import { useEffect, useRef } from '@wordpress/element'; import { getQueryArg } from '@wordpress/url'; -import { dispatch } from '@wordpress/data'; +import { dispatch, select } from '@wordpress/data'; /** * Internal dependencies @@ -72,17 +72,20 @@ export const Edit = ( { } = attributes; // This focuses on the block when a certain query param is found. This is used on the link from the task list. - const focus = getQueryArg( window.location.href, 'focus' ); + const focus = useRef( getQueryArg( window.location.href, 'focus' ) ); useEffect( () => { - if ( focus === 'checkout' ) { + if ( + focus.current === 'checkout' && + ! select( 'core/block-editor' ).hasSelectedBlock() + ) { dispatch( 'core/block-editor' ).selectBlock( clientId ); dispatch( 'core/interface' ).enableComplementaryArea( 'core/edit-site', 'edit-site/block-inspector' ); } - }, [ clientId, focus ] ); + }, [ clientId ] ); const defaultTemplate = [ [ 'woocommerce/checkout-fields-block', {}, [] ], From c2790933180499efa1d512117de4daa20142f233 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 19 Oct 2023 15:43:39 +0100 Subject: [PATCH 28/29] Revert "Removed undo link when converting from classic shortcode" This reverts commit 2babbab4c1e69861a0371ff745e85d80ff6bbab1. --- assets/js/blocks/classic-shortcode/index.tsx | 43 +++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/assets/js/blocks/classic-shortcode/index.tsx b/assets/js/blocks/classic-shortcode/index.tsx index 29a03b6b5b2..2ffe5dd8d64 100644 --- a/assets/js/blocks/classic-shortcode/index.tsx +++ b/assets/js/blocks/classic-shortcode/index.tsx @@ -1,7 +1,11 @@ /** * External dependencies */ -import { registerBlockType } from '@wordpress/blocks'; +import { + BlockInstance, + createBlock, + registerBlockType, +} from '@wordpress/blocks'; import type { BlockEditProps } from '@wordpress/blocks'; import { useBlockProps, @@ -21,6 +25,7 @@ import { useDispatch, useSelect } from '@wordpress/data'; import { useState, createInterpolateElement } from '@wordpress/element'; import { store as noticesStore } from '@wordpress/notices'; import { woo } from '@woocommerce/icons'; +import { findBlock } from '@woocommerce/utils'; /** * Internal dependencies @@ -86,6 +91,42 @@ const ConvertTemplate = ( { blockifyConfig, clientId, attributes } ) => { 'woo-gutenberg-products-block' ), { + actions: [ + { + label: __( + 'Undo', + 'woo-gutenberg-products-block' + ), + onClick: () => { + const targetBlocks = [ + 'woocommerce/cart', + 'woocommerce/checkout', + ]; + const cartCheckoutBlock = findBlock( { + blocks: getBlocks(), + findCondition: ( + foundBlock: BlockInstance + ) => + targetBlocks.includes( + foundBlock.name + ), + } ); + if ( ! cartCheckoutBlock ) { + return; + } + replaceBlock( + cartCheckoutBlock.clientId, + createBlock( + 'woocommerce/classic-shortcode', + { + shortcode: + attributes.shortcode, + } + ) + ); + }, + }, + ], type: 'snackbar', } ); From a82f9d1c27e7a5a1eacd1ad01e6a3caf50488a9a Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Thu, 19 Oct 2023 15:44:37 +0100 Subject: [PATCH 29/29] update snackbar text --- assets/js/blocks/classic-shortcode/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/blocks/classic-shortcode/index.tsx b/assets/js/blocks/classic-shortcode/index.tsx index 2ffe5dd8d64..4172a7682d6 100644 --- a/assets/js/blocks/classic-shortcode/index.tsx +++ b/assets/js/blocks/classic-shortcode/index.tsx @@ -87,7 +87,7 @@ const ConvertTemplate = ( { blockifyConfig, clientId, attributes } ) => { } ); createInfoNotice( __( - 'Classic shortcode transformed into blocks!', + 'Classic shortcode transformed to blocks.', 'woo-gutenberg-products-block' ), {