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 ( +
+ +
+ ); +}; + 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 && ( -
- -
- ) } + { 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'] ); + } + } } /**