Add Related Products block (https://github.com/woocommerce/woocommerce-blocks/pull/8522)
This commit is contained in:
parent
9bfc8a633c
commit
abd8cdbcc2
|
@ -15,3 +15,4 @@ import './product-elements/stock-indicator';
|
|||
import './product-elements/add-to-cart';
|
||||
import './product-elements/product-image-gallery';
|
||||
import './product-elements/product-details';
|
||||
import './product-elements/related-products';
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "woocommerce/related-products",
|
||||
"version": "1.0.0",
|
||||
"title": "Related Products",
|
||||
"icon": "product",
|
||||
"description": "Display related products.",
|
||||
"category": "woocommerce",
|
||||
"supports": {
|
||||
"align": true,
|
||||
"reusable": false
|
||||
},
|
||||
"keywords": [ "WooCommerce" ],
|
||||
"usesContext": [ "postId", "postType", "queryId" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2,
|
||||
"$schema": "https://schemas.wp.org/trunk/block.json"
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
BLOCK_ATTRIBUTES,
|
||||
INNER_BLOCKS_TEMPLATE,
|
||||
} from '@woocommerce/blocks/product-query/variations';
|
||||
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
|
||||
import { InnerBlockTemplate } from '@wordpress/blocks';
|
||||
import { Disabled, Notice } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './editor.scss';
|
||||
|
||||
const Edit = () => {
|
||||
const TEMPLATE: InnerBlockTemplate[] = [
|
||||
[ 'core/query', BLOCK_ATTRIBUTES, INNER_BLOCKS_TEMPLATE ],
|
||||
];
|
||||
const blockProps = useBlockProps();
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<Disabled>
|
||||
<Notice
|
||||
className={ 'wc-block-editor-related-products__notice' }
|
||||
status={ 'warning' }
|
||||
isDismissible={ false }
|
||||
>
|
||||
<p>
|
||||
{ __(
|
||||
'These products will vary depending on the main product in the page',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</p>
|
||||
</Notice>
|
||||
</Disabled>
|
||||
<InnerBlocks template={ TEMPLATE } />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Edit;
|
|
@ -0,0 +1,4 @@
|
|||
.wc-block-editor-related-products__notice {
|
||||
margin: 10px auto;
|
||||
max-width: max-content;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { box as icon } from '@wordpress/icons';
|
||||
import { registerBlockType, unregisterBlockType } from '@wordpress/blocks';
|
||||
import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import edit from './edit';
|
||||
import save from './save';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerBlockSingleProductTemplate( {
|
||||
registerBlockFn: () => {
|
||||
// @ts-expect-error: `registerBlockType` is a function that is typed in WordPress core.
|
||||
registerBlockType( metadata, {
|
||||
icon,
|
||||
edit,
|
||||
save,
|
||||
} );
|
||||
},
|
||||
unregisterBlockFn: () => {
|
||||
unregisterBlockType( metadata.name );
|
||||
},
|
||||
blockName: metadata.name,
|
||||
} );
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
|
||||
|
||||
const Save = () => {
|
||||
const blockProps = useBlockProps.save();
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
{ /* @ts-expect-error: `InnerBlocks.Content` is a component that is typed in WordPress core*/ }
|
||||
<InnerBlocks.Content />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Save;
|
|
@ -14,6 +14,7 @@ import { CORE_NAME as PRODUCT_TEMPLATE_ID } from './variations/elements/product-
|
|||
import './inspector-controls';
|
||||
import './style.scss';
|
||||
import './variations/product-query';
|
||||
import './variations/related-products';
|
||||
|
||||
const EXTENDED_CORE_ELEMENTS = [
|
||||
PRODUCT_SUMMARY_ID,
|
||||
|
|
|
@ -112,4 +112,5 @@ export interface ProductQueryContext {
|
|||
export enum QueryVariation {
|
||||
/** The main, fully customizable, Product Query block */
|
||||
PRODUCT_QUERY = 'woocommerce/product-query',
|
||||
RELATED_PRODUCTS = 'woocommerce/related-products',
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './related-products';
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import {
|
||||
InnerBlockTemplate,
|
||||
registerBlockVariation,
|
||||
unregisterBlockVariation,
|
||||
} from '@wordpress/blocks';
|
||||
import { Icon } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { stacks } from '@woocommerce/icons';
|
||||
import { registerBlockSingleProductTemplate } from '@woocommerce/atomic-utils';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { QUERY_LOOP_ID } from '../constants';
|
||||
|
||||
import { VARIATION_NAME as PRODUCT_TEMPLATE_ID } from './elements/product-template';
|
||||
import { VARIATION_NAME as PRODUCT_TITLE_ID } from './elements/product-title';
|
||||
|
||||
const VARIATION_NAME = 'woocommerce/related-products';
|
||||
|
||||
export const BLOCK_ATTRIBUTES = {
|
||||
namespace: VARIATION_NAME,
|
||||
allowedControls: [],
|
||||
displayLayout: {
|
||||
type: 'flex',
|
||||
columns: 5,
|
||||
},
|
||||
query: {
|
||||
perPage: 5,
|
||||
pages: 0,
|
||||
offset: 0,
|
||||
postType: 'product',
|
||||
order: 'asc',
|
||||
orderBy: 'title',
|
||||
author: '',
|
||||
search: '',
|
||||
exclude: [],
|
||||
sticky: '',
|
||||
inherit: false,
|
||||
},
|
||||
lock: {
|
||||
remove: true,
|
||||
move: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [
|
||||
[
|
||||
'core/post-template',
|
||||
{ __woocommerceNamespace: PRODUCT_TEMPLATE_ID },
|
||||
[
|
||||
[
|
||||
'woocommerce/product-image',
|
||||
{
|
||||
productId: 0,
|
||||
},
|
||||
],
|
||||
[
|
||||
'core/post-title',
|
||||
{
|
||||
textAlign: 'center',
|
||||
level: 3,
|
||||
fontSize: 'medium',
|
||||
__woocommerceNamespace: PRODUCT_TITLE_ID,
|
||||
},
|
||||
[],
|
||||
],
|
||||
[
|
||||
'woocommerce/product-price',
|
||||
{
|
||||
textAlign: 'center',
|
||||
fontSize: 'small',
|
||||
style: {
|
||||
spacing: {
|
||||
margin: { bottom: '1rem' },
|
||||
},
|
||||
},
|
||||
},
|
||||
[],
|
||||
],
|
||||
[
|
||||
'woocommerce/product-button',
|
||||
{
|
||||
textAlign: 'center',
|
||||
fontSize: 'small',
|
||||
style: {
|
||||
spacing: {
|
||||
margin: { bottom: '1rem' },
|
||||
},
|
||||
},
|
||||
},
|
||||
[],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
registerBlockSingleProductTemplate( {
|
||||
registerBlockFn: () =>
|
||||
registerBlockVariation( QUERY_LOOP_ID, {
|
||||
description: __(
|
||||
'Display related products.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
name: 'Related Products Controls',
|
||||
title: __(
|
||||
'Related Products Controls',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
isActive: ( blockAttributes ) =>
|
||||
blockAttributes.namespace === VARIATION_NAME,
|
||||
icon: (
|
||||
<Icon
|
||||
icon={ stacks }
|
||||
className="wc-block-editor-components-block-icon wc-block-editor-components-block-icon--stacks"
|
||||
/>
|
||||
),
|
||||
attributes: BLOCK_ATTRIBUTES,
|
||||
// Gutenberg doesn't support this type yet, discussion here:
|
||||
// https://github.com/WordPress/gutenberg/pull/43632
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
allowedControls: [],
|
||||
innerBlocks: INNER_BLOCKS_TEMPLATE,
|
||||
scope: [ 'block' ],
|
||||
} ),
|
||||
unregisterBlockFn: () =>
|
||||
unregisterBlockVariation( QUERY_LOOP_ID, 'Related Products' ),
|
||||
blockName: VARIATION_NAME,
|
||||
} );
|
|
@ -81,7 +81,7 @@ class ProductQuery extends AbstractBlock {
|
|||
* @param array $parsed_block The block being rendered.
|
||||
* @return boolean
|
||||
*/
|
||||
private function is_woocommerce_variation( $parsed_block ) {
|
||||
public static function is_woocommerce_variation( $parsed_block ) {
|
||||
return isset( $parsed_block['attrs']['namespace'] )
|
||||
&& substr( $parsed_block['attrs']['namespace'], 0, 11 ) === 'woocommerce';
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ class ProductQuery extends AbstractBlock {
|
|||
|
||||
$this->parsed_block = $parsed_block;
|
||||
|
||||
if ( $this->is_woocommerce_variation( $parsed_block ) ) {
|
||||
if ( self::is_woocommerce_variation( $parsed_block ) ) {
|
||||
// Set this so that our product filters can detect if it's a PHP template.
|
||||
$this->asset_data_registry->add( 'has_filterable_products', true, true );
|
||||
$this->asset_data_registry->add( 'is_rendering_php_template', true, true );
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
/**
|
||||
* RelatedProducts class.
|
||||
*/
|
||||
class RelatedProducts extends AbstractBlock {
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'related-products';
|
||||
|
||||
/**
|
||||
* The Block with its attributes before it gets rendered
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $parsed_block;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize this block type.
|
||||
*
|
||||
* - Hook into WP lifecycle.
|
||||
* - Register the block with WordPress.
|
||||
* - Hook into pre_render_block to update the query.
|
||||
*/
|
||||
protected function initialize() {
|
||||
parent::initialize();
|
||||
add_filter(
|
||||
'pre_render_block',
|
||||
array( $this, 'update_query' ),
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the query for the product query block.
|
||||
*
|
||||
* @param string|null $pre_render The pre-rendered content. Default null.
|
||||
* @param array $parsed_block The block being rendered.
|
||||
*/
|
||||
public function update_query( $pre_render, $parsed_block ) {
|
||||
if ( 'core/query' !== $parsed_block['blockName'] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->parsed_block = $parsed_block;
|
||||
|
||||
if ( ProductQuery::is_woocommerce_variation( $parsed_block ) && 'woocommerce/related-products' === $parsed_block['attrs']['namespace'] ) {
|
||||
// Set this so that our product filters can detect if it's a PHP template.
|
||||
add_filter(
|
||||
'query_loop_block_query_vars',
|
||||
array( $this, 'build_query' ),
|
||||
10,
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a custom query based on attributes, filters and global WP_Query.
|
||||
*
|
||||
* @param WP_Query $query The WordPress Query.
|
||||
* @return array
|
||||
*/
|
||||
public function build_query( $query ) {
|
||||
$parsed_block = $this->parsed_block;
|
||||
if ( ! ProductQuery::is_woocommerce_variation( $parsed_block ) && 'woocommerce/related-products' !== $parsed_block['attrs']['namespace'] ) {
|
||||
return $query;
|
||||
}
|
||||
$product = wc_get_product();
|
||||
$related_products = wc_get_related_products( $product->get_id() );
|
||||
|
||||
return array(
|
||||
'post_type' => 'product',
|
||||
'post__in' => $related_products,
|
||||
'post_status' => 'publish',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -212,6 +212,7 @@ final class BlockTypesController {
|
|||
'RatingFilter',
|
||||
'ReviewsByCategory',
|
||||
'ReviewsByProduct',
|
||||
'RelatedProducts',
|
||||
'ProductDetails',
|
||||
'StockFilter',
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue