diff --git a/assets/js/blocks/classic-template/archive-product.ts b/assets/js/blocks/classic-template/archive-product.ts
index dd93f0b10e0..1a5f391f65d 100644
--- a/assets/js/blocks/classic-template/archive-product.ts
+++ b/assets/js/blocks/classic-template/archive-product.ts
@@ -147,18 +147,26 @@ const onClickCallbackWithTermDescription = ( {
}
};
-export const blockifiedProductCatalogConfig = {
+const productCatalogBlockifyConfig = {
+ getButtonLabel,
+ onClickCallback,
getBlockifiedTemplate,
+};
+
+const productTaxonomyBlockifyConfig = {
+ getButtonLabel,
+ onClickCallback: onClickCallbackWithTermDescription,
+ getBlockifiedTemplate: getBlockifiedTemplateWithTermDescription,
+};
+
+export const blockifiedProductCatalogConfig = {
isConversionPossible,
getDescription,
- getButtonLabel,
- onClickCallback,
+ blockifyConfig: productCatalogBlockifyConfig,
};
export const blockifiedProductTaxonomyConfig = {
- getBlockifiedTemplate: getBlockifiedTemplateWithTermDescription,
- onClickCallback: onClickCallbackWithTermDescription,
isConversionPossible,
getDescription,
- getButtonLabel,
+ blockifyConfig: productTaxonomyBlockifyConfig,
};
diff --git a/assets/js/blocks/classic-template/editor.scss b/assets/js/blocks/classic-template/editor.scss
index c2997f22d81..232285a91d5 100644
--- a/assets/js/blocks/classic-template/editor.scss
+++ b/assets/js/blocks/classic-template/editor.scss
@@ -3,55 +3,86 @@
margin-right: auto;
}
+.wp-block-woocommerce-classic-template__placeholder-warning {
+ border-left: 5px solid #2181d2;
+ padding-left: em(40px);
+}
+.wp-block-woocommerce-classic-template__placeholder .components-placeholder__fieldset {
+ display: grid;
+ grid-template-columns: 1fr;
+}
+.wp-block-woocommerce-classic-template__placeholder-wireframe,
+.wp-block-woocommerce-classic-template__placeholder-copy {
+ grid-row-start: 1;
+ grid-column-start: 1;
+ transition: 0.3s all ease;
+}
.wp-block-woocommerce-classic-template__placeholder-copy {
+ border: 1px solid $gray-900;
+ background-color: #fff;
+ padding: $gap-large $gap-larger;
+ border-radius: 3px;
display: flex;
flex-direction: column;
max-width: 900px;
width: 400px;
margin: auto;
-}
+ opacity: 0;
+ z-index: 10;
-.wp-block-woocommerce-classic-template__placeholder-warning {
- border-left: 5px solid #2181d2;
- padding-left: em(40px);
-}
+ .wp-block-woocommerce-classic-template__placeholder-copy__icon-container {
+ margin: 0 0 $gap;
-.wp-block-woocommerce-classic-template__placeholder-wireframe {
- height: 250px;
- background: #e5e5e5;
- display: flex;
- flex-wrap: wrap;
- gap: $gap-large;
- margin: auto;
+ span {
+ @include font-size(larger);
+ display: block;
+ }
+ .woo-icon {
+ color: #{$studio-woocommerce-purple};
+ @include font-size(large);
- @media only screen and (min-width: 768px) {
- height: auto;
- background: transparent;
+ svg {
+ vertical-align: middle;
+ }
+ }
+ }
+ p {
+ margin: 0 0 $gap;
+ }
+ .wp-block-woocommerce-classic-template__placeholder-migration-button-container {
+ justify-content: center;
+ margin: $gap 0;
}
}
+.wp-block-woocommerce-classic-template__placeholder-wireframe {
+ pointer-events: none;
-.wp-block-woocommerce-classic-template__placeholder .wp-block-woocommerce-classic-template__placeholder-image {
- display: none;
- width: 400px;
- height: auto;
-
- @media only screen and (min-width: 768px) {
- display: block;
+ // Image based placeholders should fill horizontal width.
+ > img {
+ width: 100%;
}
}
-.wp-block-woocommerce-classic-template__placeholder-migration-button-container {
- justify-content: center;
- align-items: center;
- margin: 0 auto;
+.wp-block-woocommerce-legacy-template {
+ .components-placeholder {
+ box-shadow: none;
+ padding: 0;
+ }
}
+.wp-block-woocommerce-legacy-template.is-selected {
+ .wp-block-woocommerce-classic-template__placeholder-wireframe {
+ filter: blur(3px);
+ opacity: 0.5;
-.wp-block-woocommerce-classic-template__placeholder-copy__icon-container {
- display: flex;
- align-items: center;
- gap: $gap-small;
-
- span {
- @include font-size(larger);
+ * {
+ color: $gray-200 !important;
+ border-color: $gray-200 !important;
+ }
+ }
+ .wp-block-woocommerce-classic-template__placeholder-copy {
+ opacity: 1;
+ }
+ .components-placeholder {
+ box-shadow: inherit;
}
}
diff --git a/assets/js/blocks/classic-template/index.tsx b/assets/js/blocks/classic-template/index.tsx
index 166e6a33ad4..6ff439acd13 100644
--- a/assets/js/blocks/classic-template/index.tsx
+++ b/assets/js/blocks/classic-template/index.tsx
@@ -30,6 +30,7 @@ import { useEffect, useState } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { useEntityRecord } from '@wordpress/core-data';
import { debounce } from '@woocommerce/base-utils';
+import { woo } from '@woocommerce/icons';
/**
* Internal dependencies
@@ -61,7 +62,6 @@ const blockifiedFallbackConfig = {
isConversionPossible: () => false,
getBlockifiedTemplate: () => [],
getDescription: () => '',
- getButtonLabel: () => '',
onClickCallback: () => void 0,
};
@@ -83,17 +83,121 @@ const pickBlockClientIds = ( blocks: Array< BlockInstance > ) =>
return [ ...acc, block.clientId ];
}, [] );
+const ConvertTemplate = ( { blockifyConfig, clientId, attributes } ) => {
+ const { getButtonLabel, onClickCallback, getBlockifiedTemplate } =
+ blockifyConfig;
+
+ const [ isPopoverOpen, setIsPopoverOpen ] = useState( false );
+ const { replaceBlock, selectBlock, replaceBlocks } =
+ useDispatch( blockEditorStore );
+
+ const { getBlocks } = useSelect( ( sel ) => {
+ return {
+ getBlocks: sel( blockEditorStore ).getBlocks,
+ };
+ }, [] );
+
+ const { createInfoNotice } = useDispatch( noticesStore );
+
+ return (
+
+
{
+ onClickCallback( {
+ clientId,
+ getBlocks,
+ attributes,
+ replaceBlock,
+ selectBlock,
+ } );
+ createInfoNotice(
+ __(
+ 'Template transformed into blocks!',
+ 'woo-gutenberg-products-block'
+ ),
+ {
+ actions: [
+ {
+ label: __(
+ 'Undo',
+ 'woo-gutenberg-products-block'
+ ),
+ onClick: () => {
+ const clientIds = pickBlockClientIds(
+ getBlocks()
+ );
+
+ replaceBlocks(
+ clientIds,
+ createBlock(
+ 'core/group',
+ {
+ layout: {
+ inherit: true,
+ type: 'constrained',
+ },
+ },
+ [
+ createBlock(
+ 'woocommerce/legacy-template',
+ {
+ template:
+ attributes.template,
+ }
+ ),
+ ]
+ )
+ );
+ },
+ },
+ ],
+ type: 'snackbar',
+ }
+ );
+ } }
+ onMouseEnter={ () => setIsPopoverOpen( true ) }
+ onMouseLeave={ () => setIsPopoverOpen( false ) }
+ text={ getButtonLabel ? getButtonLabel() : '' }
+ >
+ { isPopoverOpen && (
+
+
+
+
+
+ ) }
+
+
+ );
+};
+
const Edit = ( {
clientId,
attributes,
setAttributes,
}: BlockEditProps< Attributes > ) => {
- const { replaceBlock, selectBlock, replaceBlocks } =
- useDispatch( blockEditorStore );
-
- const { getBlocks, editedPostId } = useSelect( ( sel ) => {
+ const blockProps = useBlockProps();
+ const { editedPostId } = useSelect( ( sel ) => {
return {
- getBlocks: sel( blockEditorStore ).getBlocks,
editedPostId: sel( 'core/edit-site' ).getEditedPostId(),
};
}, [] );
@@ -106,9 +210,6 @@ const Edit = ( {
};
} >( 'postType', 'wp_template', editedPostId );
- const { createInfoNotice } = useDispatch( noticesStore );
-
- const blockProps = useBlockProps();
const templateDetails = getTemplateDetailsBySlug(
attributes.template,
TEMPLATES
@@ -130,136 +231,59 @@ const Edit = ( {
const {
isConversionPossible,
getDescription,
- getButtonLabel,
- onClickCallback,
- getBlockifiedTemplate,
+ getSkeleton,
+ blockifyConfig,
} = conversionConfig[ templateType ];
+ const skeleton = getSkeleton ? (
+ getSkeleton()
+ ) : (
+
+ );
+
const canConvert = isConversionPossible();
const placeholderDescription = getDescription( templateTitle, canConvert );
- const [ isPopoverOpen, setIsPopoverOpen ] = useState( false );
return (
-
-
-
-
- { __(
- 'Classic Product Template',
- 'woo-gutenberg-products-block'
- ) }
-
-
-
{ placeholderDescription }
- { canConvert && (
-
-
{
- onClickCallback( {
- clientId,
- getBlocks,
- attributes,
- replaceBlock,
- selectBlock,
- } );
- createInfoNotice(
- __(
- 'Template transformed into blocks!',
- 'woo-gutenberg-products-block'
- ),
- {
- actions: [
- {
- label: __(
- 'Undo',
- 'woo-gutenberg-products-block'
- ),
- onClick: () => {
- const clientIds =
- pickBlockClientIds(
- getBlocks()
- );
-
- replaceBlocks(
- clientIds,
- createBlock(
- 'core/group',
- {
- layout: {
- inherit:
- true,
- type: 'constrained',
- },
- },
- [
- createBlock(
- 'woocommerce/legacy-template',
- {
- template:
- attributes.template,
- }
- ),
- ]
- )
- );
- },
- },
- ],
- type: 'snackbar',
- }
- );
- } }
- onMouseEnter={ () =>
- setIsPopoverOpen( true )
- }
- onMouseLeave={ () =>
- setIsPopoverOpen( false )
- }
- text={ getButtonLabel() }
- >
- { isPopoverOpen && (
-
-
-
-
-
- ) }
-
-
- ) }
+ { skeleton }
+
+
+
+
+ WooCommerce
+
+
+ { __(
+ 'Classic Template Placeholder',
+ 'woo-gutenberg-products-block'
+ ) }
+
-
+
+ { __(
+ 'You cannot edit the content of this block. However, you can move it and place other blocks around it.',
+ 'woo-gutenberg-products-block'
+ ) }
+
+ { canConvert && blockifyConfig && (
+
+ ) }
diff --git a/assets/js/blocks/classic-template/order-received.ts b/assets/js/blocks/classic-template/order-received.ts
deleted file mode 100644
index 9df2ebf7a49..00000000000
--- a/assets/js/blocks/classic-template/order-received.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * External dependencies
- */
-
-import { __, sprintf } from '@wordpress/i18n';
-
-/**
- * Internal dependencies
- */
-
-const getBlockifiedTemplate = () => [];
-
-const isConversionPossible = () => {
- // Blockification is possible for the WP version 6.1 and above,
- // which are the versions the Products block supports.
- return false;
-};
-
-const getDescriptionAllowingConversion = ( templateTitle: string ) =>
- sprintf(
- /* translators: %s is the template title */
- __(
- "This block serves as a placeholder for your %s. We recommend upgrading to the Order Received block for more features and to edit it visually. Don't worry, you can always revert back.",
- 'woo-gutenberg-products-block'
- ),
- templateTitle
- );
-
-const getDescriptionDisallowingConversion = ( templateTitle: string ) =>
- sprintf(
- /* translators: %s is the template title */
- __(
- 'This block serves as a placeholder for your %s. It will display the thankyou.php template. You can move this placeholder around and add more blocks around to customize the template.',
- 'woo-gutenberg-products-block'
- ),
- templateTitle
- );
-
-const getDescription = ( templateTitle: string, canConvert: boolean ) => {
- if ( canConvert ) {
- return getDescriptionAllowingConversion( templateTitle );
- }
-
- return getDescriptionDisallowingConversion( templateTitle );
-};
-
-const getButtonLabel = () =>
- __( 'Upgrade to Products block', 'woo-gutenberg-products-block' );
-
-export {
- getBlockifiedTemplate,
- isConversionPossible,
- getDescription,
- getButtonLabel,
-};
diff --git a/assets/js/blocks/classic-template/order-received.tsx b/assets/js/blocks/classic-template/order-received.tsx
new file mode 100644
index 00000000000..2408f344151
--- /dev/null
+++ b/assets/js/blocks/classic-template/order-received.tsx
@@ -0,0 +1,153 @@
+/**
+ * External dependencies
+ */
+
+import { __ } from '@wordpress/i18n';
+
+const isConversionPossible = () => {
+ return false;
+};
+
+const getDescription = () => {
+ return __(
+ 'This block represents the thankyou.php classic template. The actual rendered template may appear different from this placeholder.',
+ 'woo-gutenberg-products-block'
+ );
+};
+
+const getSkeleton = () => {
+ return (
+
+
+
+ { __( 'Order received', 'woo-gutenberg-products-block' ) }
+
+
+ { __(
+ 'Thank you. Your order has been received.',
+ 'woo-gutenberg-products-block'
+ ) }
+
+
+
+ { __( 'Order number', 'woo-gutenberg-products-block' ) }
+ : 123
+
+
+ { __( 'Date', 'woo-gutenberg-products-block' ) }:{ ' ' }
+ May 25, 2023
+
+
+ { __( 'Email', 'woo-gutenberg-products-block' ) }:{ ' ' }
+ shopper@woo.com
+
+
+ { __( 'Total', 'woo-gutenberg-products-block' ) }:{ ' ' }
+ $20.00
+
+
+
+
+
+ { __(
+ 'Order details',
+ 'woo-gutenberg-products-block'
+ ) }
+
+
+
+
+
+ { __(
+ 'Product',
+ 'woo-gutenberg-products-block'
+ ) }
+
+
+ { __(
+ 'Total',
+ 'woo-gutenberg-products-block'
+ ) }
+
+
+
+
+
+
+ Sample Product{ ' ' }
+
+ × 2
+ { ' ' }
+
+
+
+ $20.00
+
+
+
+
+
+
+ { __(
+ 'Subtotal',
+ 'woo-gutenberg-products-block'
+ ) }
+ :
+
+ $20.00
+
+
+
+ { __(
+ 'Total',
+ 'woo-gutenberg-products-block'
+ ) }
+ :
+
+ $20.00
+
+
+
+
+
+
+
+
+
+ { __(
+ 'Billing address',
+ 'woo-gutenberg-products-block'
+ ) }
+
+
+ 123 Main St
+
+ New York, NY 10001
+
+ United States (US)
+
+
+
+
+
+ { __(
+ 'Shipping address',
+ 'woo-gutenberg-products-block'
+ ) }
+
+
+ 123 Main St
+
+ New York, NY 10001
+
+ United States (US)
+
+
+
+
+
+
+ );
+};
+
+export { isConversionPossible, getDescription, getSkeleton };
diff --git a/assets/js/blocks/classic-template/product-search-results.ts b/assets/js/blocks/classic-template/product-search-results.ts
index 44587355401..6ea531e22f2 100644
--- a/assets/js/blocks/classic-template/product-search-results.ts
+++ b/assets/js/blocks/classic-template/product-search-results.ts
@@ -169,10 +169,10 @@ const onClickCallback = ( {
const getButtonLabel = () =>
__( 'Transform into blocks', 'woo-gutenberg-products-block' );
-export {
- getBlockifiedTemplate,
- isConversionPossible,
- getDescription,
+const blockifyConfig = {
getButtonLabel,
onClickCallback,
+ getBlockifiedTemplate,
};
+
+export { isConversionPossible, getDescription, blockifyConfig };
diff --git a/assets/js/blocks/classic-template/single-product.ts b/assets/js/blocks/classic-template/single-product.ts
index 95ed17dbe84..419d36d49c8 100644
--- a/assets/js/blocks/classic-template/single-product.ts
+++ b/assets/js/blocks/classic-template/single-product.ts
@@ -114,10 +114,10 @@ const onClickCallback = ( {
}
};
-export {
- getBlockifiedTemplate,
- isConversionPossible,
- getDescription,
+const blockifyConfig = {
getButtonLabel,
onClickCallback,
+ getBlockifiedTemplate,
};
+
+export { isConversionPossible, getDescription, blockifyConfig };
diff --git a/assets/js/blocks/classic-template/types.ts b/assets/js/blocks/classic-template/types.ts
index f9274706792..30995413933 100644
--- a/assets/js/blocks/classic-template/types.ts
+++ b/assets/js/blocks/classic-template/types.ts
@@ -23,12 +23,21 @@ export type OnClickCallbackParameter = {
selectBlock: ( clientId: string ) => void;
};
-export type BlockifiedTemplateConfig = {
+type ConversionConfig = {
+ onClickCallback: ( params: OnClickCallbackParameter ) => void;
+ getButtonLabel: () => string;
getBlockifiedTemplate: (
inheritedAttributes: InheritedAttributes
) => BlockInstance[];
- isConversionPossible: () => boolean;
+};
+
+export type BlockifiedTemplateConfig = {
+ // Description of the template, shown in the block placeholder.
getDescription: ( templateTitle: string, canConvert: boolean ) => string;
- getButtonLabel: () => string;
- onClickCallback: ( params: OnClickCallbackParameter ) => void;
+ // Returns the skeleton HTML for the template, or can be left blank to use the default fallback image.
+ getSkeleton?: ( () => JSX.Element ) | undefined;
+ // Is conversion possible for the template?
+ isConversionPossible: () => boolean;
+ // If conversion is possible, returns the config for the template to be blockified.
+ blockifyConfig?: ConversionConfig | undefined;
};
diff --git a/src/BlockTypes/ClassicTemplate.php b/src/BlockTypes/ClassicTemplate.php
index 090f15b8e8d..e61e84771fa 100644
--- a/src/BlockTypes/ClassicTemplate.php
+++ b/src/BlockTypes/ClassicTemplate.php
@@ -36,7 +36,23 @@ protected function initialize() {
parent::initialize();
add_filter( 'render_block', array( $this, 'add_alignment_class_to_wrapper' ), 10, 2 );
add_filter( 'woocommerce_product_query_meta_query', array( $this, 'filter_products_by_stock' ) );
+ add_action( 'enqueue_block_assets', array( $this, 'enqueue_block_assets' ) );
+ }
+ /**
+ * Enqueue assets used for rendering the block in editor context.
+ *
+ * This is needed if a block is not yet within the post content--`render` and `enqueue_assets` may not have ran.
+ */
+ public function enqueue_block_assets() {
+ // Ensures frontend styles for blocks exist in the site editor iframe.
+ if ( class_exists( 'WC_Frontend_Scripts' ) && is_admin() ) {
+ $frontend_scripts = new \WC_Frontend_Scripts();
+ $styles = $frontend_scripts::get_styles();
+ foreach ( $styles as $handle => $style ) {
+ wp_enqueue_style( $handle, $style['src'], $style['deps'], $style['version'], $style['media'] );
+ }
+ }
}
/**