Refactor Product Query to use the latest Gutenberg APIs (https://github.com/woocommerce/woocommerce-blocks/pull/7169)
* Refactor Product Query to use the latest Gutenberg APIs As we worked with Gutenberg folks in WordPress/gutenbergwoocommerce/woocommerce-blocks#43590, WordPress/gutenbergwoocommerce/woocommerce-blocks#43632 and WordPress/gutenbergwoocommerce/woocommerce-blocks#44093 we have created a standard API that could be used for our use-case. This PR refactors our WIP experimental work to use that standardized API.
This commit is contained in:
parent
6b0407d200
commit
d174051787
|
@ -1,20 +1,24 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { InnerBlockTemplate } from '@wordpress/blocks';
|
||||
import type { InnerBlockTemplate } from '@wordpress/blocks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { QueryBlockQuery } from './types';
|
||||
import { QueryBlockAttributes } from './types';
|
||||
|
||||
export const QUERY_DEFAULT_ATTRIBUTES: {
|
||||
query: QueryBlockQuery;
|
||||
displayLayout: {
|
||||
type: 'flex' | 'list';
|
||||
columns?: number;
|
||||
};
|
||||
} = {
|
||||
export const DEFAULT_CORE_ALLOWED_CONTROLS = [ 'order', 'taxQuery', 'search' ];
|
||||
|
||||
export const ALL_PRODUCT_QUERY_CONTROLS = [ 'onSale' ];
|
||||
|
||||
export const DEFAULT_ALLOWED_CONTROLS = [
|
||||
...DEFAULT_CORE_ALLOWED_CONTROLS,
|
||||
...ALL_PRODUCT_QUERY_CONTROLS,
|
||||
];
|
||||
|
||||
export const QUERY_DEFAULT_ATTRIBUTES: QueryBlockAttributes = {
|
||||
allowControls: DEFAULT_ALLOWED_CONTROLS,
|
||||
displayLayout: {
|
||||
type: 'flex',
|
||||
columns: 3,
|
||||
|
@ -39,7 +43,7 @@ export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [
|
|||
'core/post-template',
|
||||
{},
|
||||
[
|
||||
[ 'woocommerce/product-image', undefined, [] ],
|
||||
[ 'woocommerce/product-image' ],
|
||||
[
|
||||
'core/post-title',
|
||||
{
|
||||
|
@ -50,6 +54,6 @@ export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [
|
|||
],
|
||||
],
|
||||
],
|
||||
[ 'core/query-pagination', undefined, [] ],
|
||||
[ 'core/query-no-results', undefined, [] ],
|
||||
[ 'core/query-pagination' ],
|
||||
[ 'core/query-no-results' ],
|
||||
];
|
||||
|
|
|
@ -12,7 +12,11 @@ import { ElementType } from 'react';
|
|||
* Internal dependencies
|
||||
*/
|
||||
import { ProductQueryBlock } from './types';
|
||||
import { isWooQueryBlockVariation, setCustomQueryAttribute } from './utils';
|
||||
import {
|
||||
isWooQueryBlockVariation,
|
||||
setCustomQueryAttribute,
|
||||
useAllowedControls,
|
||||
} from './utils';
|
||||
|
||||
export const INSPECTOR_CONTROLS = {
|
||||
onSale: ( props: ProductQueryBlock ) => (
|
||||
|
@ -21,12 +25,9 @@ export const INSPECTOR_CONTROLS = {
|
|||
'Show only products on sale',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
checked={
|
||||
props.attributes.__woocommerceVariationProps?.attributes?.query
|
||||
?.onSale || false
|
||||
}
|
||||
onChange={ ( onSale ) => {
|
||||
setCustomQueryAttribute( props, { onSale } );
|
||||
checked={ props.attributes.query.__woocommerceOnSale || false }
|
||||
onChange={ ( __woocommerceOnSale ) => {
|
||||
setCustomQueryAttribute( props, { __woocommerceOnSale } );
|
||||
} }
|
||||
/>
|
||||
),
|
||||
|
@ -35,17 +36,16 @@ export const INSPECTOR_CONTROLS = {
|
|||
export const withProductQueryControls =
|
||||
< T extends EditorBlock< T > >( BlockEdit: ElementType ) =>
|
||||
( props: ProductQueryBlock ) => {
|
||||
const allowedControls = useAllowedControls( props.attributes );
|
||||
return isWooQueryBlockVariation( props ) ? (
|
||||
<>
|
||||
<BlockEdit { ...props } />
|
||||
<InspectorControls>
|
||||
{ Object.entries( INSPECTOR_CONTROLS ).map(
|
||||
( [ key, Control ] ) =>
|
||||
props.attributes.__woocommerceVariationProps.attributes?.disabledInspectorControls?.includes(
|
||||
key
|
||||
) ? null : (
|
||||
allowedControls?.includes( key ) ? (
|
||||
<Control { ...props } />
|
||||
)
|
||||
) : null
|
||||
) }
|
||||
</InspectorControls>
|
||||
</>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { BlockInstance } from '@wordpress/blocks';
|
||||
import type { EditorBlock } from '@woocommerce/types';
|
||||
|
||||
export interface ProductQueryArguments {
|
||||
|
@ -28,11 +27,14 @@ export interface ProductQueryArguments {
|
|||
* )
|
||||
* ```
|
||||
*/
|
||||
onSale?: boolean;
|
||||
// Disabling naming convention because we are namespacing our
|
||||
// custom attributes inside a core block. Prefixing with underscores
|
||||
// will help signify our intentions.
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
__woocommerceOnSale?: boolean;
|
||||
}
|
||||
|
||||
export type ProductQueryBlock =
|
||||
WooCommerceBlockVariation< ProductQueryAttributes >;
|
||||
export type ProductQueryBlock = EditorBlock< QueryBlockAttributes >;
|
||||
|
||||
export interface ProductQueryAttributes {
|
||||
/**
|
||||
|
@ -47,6 +49,16 @@ export interface ProductQueryAttributes {
|
|||
query?: ProductQueryArguments;
|
||||
}
|
||||
|
||||
export interface QueryBlockAttributes {
|
||||
allowControls?: string[];
|
||||
displayLayout?: {
|
||||
type: 'flex' | 'list';
|
||||
columns?: number;
|
||||
};
|
||||
namespace?: string;
|
||||
query: QueryBlockQuery & ProductQueryArguments;
|
||||
}
|
||||
|
||||
export interface QueryBlockQuery {
|
||||
author?: string;
|
||||
exclude?: string[];
|
||||
|
@ -65,15 +77,7 @@ export interface QueryBlockQuery {
|
|||
|
||||
export enum QueryVariation {
|
||||
/** The main, fully customizable, Product Query block */
|
||||
PRODUCT_QUERY = 'product-query',
|
||||
PRODUCT_QUERY = 'woocommerce/product-query',
|
||||
/** Only shows products on sale */
|
||||
PRODUCTS_ON_SALE = 'query-products-on-sale',
|
||||
PRODUCTS_ON_SALE = 'woocommerce/query-products-on-sale',
|
||||
}
|
||||
|
||||
export type WooCommerceBlockVariation< T > = EditorBlock< {
|
||||
// Disabling naming convention because we are namespacing our
|
||||
// custom attributes inside a core block. Prefixing with underscores
|
||||
// will help signify our intentions.
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
__woocommerceVariationProps: Partial< BlockInstance< T > >;
|
||||
} >;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { store as WP_BLOCKS_STORE } from '@wordpress/blocks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
|
@ -7,6 +13,13 @@ import {
|
|||
QueryVariation,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* Creates an array that is the symmetric difference of the given arrays
|
||||
*/
|
||||
export function ArrayXOR< T extends Array< unknown > >( a: T, b: T ) {
|
||||
return a.filter( ( el ) => ! b.includes( el ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if a block is a Query block variation from our conventions
|
||||
*
|
||||
|
@ -17,10 +30,8 @@ import {
|
|||
export function isWooQueryBlockVariation( block: ProductQueryBlock ) {
|
||||
return (
|
||||
block.name === 'core/query' &&
|
||||
block.attributes.__woocommerceVariationProps &&
|
||||
Object.values( QueryVariation ).includes(
|
||||
block.attributes.__woocommerceVariationProps
|
||||
.name as unknown as QueryVariation
|
||||
block.attributes.namespace as QueryVariation
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -35,20 +46,35 @@ export function isWooQueryBlockVariation( block: ProductQueryBlock ) {
|
|||
*/
|
||||
export function setCustomQueryAttribute(
|
||||
block: ProductQueryBlock,
|
||||
attributes: Partial< ProductQueryArguments >
|
||||
queryParams: Partial< ProductQueryArguments >
|
||||
) {
|
||||
const { __woocommerceVariationProps } = block.attributes;
|
||||
const { query } = block.attributes;
|
||||
|
||||
block.setAttributes( {
|
||||
__woocommerceVariationProps: {
|
||||
...__woocommerceVariationProps,
|
||||
attributes: {
|
||||
...__woocommerceVariationProps.attributes,
|
||||
query: {
|
||||
...__woocommerceVariationProps.attributes?.query,
|
||||
...attributes,
|
||||
},
|
||||
},
|
||||
...query,
|
||||
...queryParams,
|
||||
},
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook that returns the query properties' names defined by the active
|
||||
* block variation, to determine which block inspector controls to show.
|
||||
*
|
||||
* @param {Object} attributes Block attributes.
|
||||
* @return {string[]} An array of the controls keys.
|
||||
*/
|
||||
export function useAllowedControls(
|
||||
attributes: ProductQueryBlock[ 'attributes' ]
|
||||
) {
|
||||
return useSelect(
|
||||
( select ) =>
|
||||
select( WP_BLOCKS_STORE ).getActiveBlockVariation(
|
||||
'core/query',
|
||||
attributes
|
||||
)?.allowControls,
|
||||
|
||||
[ attributes ]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,18 +10,20 @@ import { sparkles } from '@wordpress/icons';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { INNER_BLOCKS_TEMPLATE, QUERY_DEFAULT_ATTRIBUTES } from '../constants';
|
||||
import {
|
||||
DEFAULT_ALLOWED_CONTROLS,
|
||||
INNER_BLOCKS_TEMPLATE,
|
||||
QUERY_DEFAULT_ATTRIBUTES,
|
||||
} from '../constants';
|
||||
|
||||
const VARIATION_NAME = 'woocommerce/product-query';
|
||||
|
||||
if ( isExperimentalBuild() ) {
|
||||
registerBlockVariation( 'core/query', {
|
||||
name: 'woocommerce/product-query',
|
||||
name: VARIATION_NAME,
|
||||
title: __( 'Product Query', 'woo-gutenberg-products-block' ),
|
||||
isActive: ( attributes ) => {
|
||||
return (
|
||||
attributes?.__woocommerceVariationProps?.name ===
|
||||
'product-query'
|
||||
);
|
||||
},
|
||||
isActive: ( blockAttributes ) =>
|
||||
blockAttributes.namespace === VARIATION_NAME,
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
|
@ -32,10 +34,13 @@ if ( isExperimentalBuild() ) {
|
|||
},
|
||||
attributes: {
|
||||
...QUERY_DEFAULT_ATTRIBUTES,
|
||||
__woocommerceVariationProps: {
|
||||
name: 'product-query',
|
||||
},
|
||||
namespace: VARIATION_NAME,
|
||||
},
|
||||
// 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
|
||||
allowControls: DEFAULT_ALLOWED_CONTROLS,
|
||||
innerBlocks: INNER_BLOCKS_TEMPLATE,
|
||||
scope: [ 'block', 'inserter' ],
|
||||
} );
|
||||
|
|
|
@ -9,17 +9,23 @@ import { Icon, percent } from '@wordpress/icons';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { INNER_BLOCKS_TEMPLATE, QUERY_DEFAULT_ATTRIBUTES } from '../constants';
|
||||
import {
|
||||
DEFAULT_CORE_ALLOWED_CONTROLS,
|
||||
INNER_BLOCKS_TEMPLATE,
|
||||
QUERY_DEFAULT_ATTRIBUTES,
|
||||
} from '../constants';
|
||||
import { ArrayXOR } from '../utils';
|
||||
|
||||
const VARIATION_NAME = 'woocommerce/query-products-on-sale';
|
||||
const DISABLED_INSPECTOR_CONTROLS = [ 'onSale' ];
|
||||
|
||||
if ( isExperimentalBuild() ) {
|
||||
registerBlockVariation( 'core/query', {
|
||||
name: 'woocommerce/query-products-on-sale',
|
||||
name: VARIATION_NAME,
|
||||
title: __( 'Products on Sale', 'woo-gutenberg-products-block' ),
|
||||
isActive: ( blockAttributes ) =>
|
||||
blockAttributes?.__woocommerceVariationProps?.name ===
|
||||
'query-products-on-sale' ||
|
||||
blockAttributes?.__woocommerceVariationProps?.query?.onSale ===
|
||||
true,
|
||||
blockAttributes.namespace === VARIATION_NAME ||
|
||||
blockAttributes.query?.__woocommerceOnSale === true,
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
|
@ -30,15 +36,20 @@ if ( isExperimentalBuild() ) {
|
|||
},
|
||||
attributes: {
|
||||
...QUERY_DEFAULT_ATTRIBUTES,
|
||||
__woocommerceVariationProps: {
|
||||
name: 'query-products-on-sale',
|
||||
attributes: {
|
||||
namespace: VARIATION_NAME,
|
||||
query: {
|
||||
onSale: true,
|
||||
},
|
||||
},
|
||||
...QUERY_DEFAULT_ATTRIBUTES.query,
|
||||
__woocommerceOnSale: true,
|
||||
},
|
||||
},
|
||||
// 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
|
||||
allowControls: ArrayXOR(
|
||||
DEFAULT_CORE_ALLOWED_CONTROLS,
|
||||
DISABLED_INSPECTOR_CONTROLS
|
||||
),
|
||||
innerBlocks: INNER_BLOCKS_TEMPLATE,
|
||||
scope: [ 'block', 'inserter' ],
|
||||
} );
|
||||
|
|
|
@ -37,6 +37,17 @@ class ProductQuery extends AbstractBlock {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given block
|
||||
*
|
||||
* @param array $parsed_block The block being rendered.
|
||||
* @return boolean
|
||||
*/
|
||||
private function is_woocommerce_variation( $parsed_block ) {
|
||||
return isset( $parsed_block['attrs']['namespace'] )
|
||||
&& substr( $parsed_block['attrs']['namespace'], 0, 11 ) === 'woocommerce';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the query for the product query block.
|
||||
|
@ -51,7 +62,7 @@ class ProductQuery extends AbstractBlock {
|
|||
|
||||
$this->parsed_block = $parsed_block;
|
||||
|
||||
if ( isset( $parsed_block['attrs']['__woocommerceVariationProps'] ) ) {
|
||||
if ( $this->is_woocommerce_variation( $parsed_block ) ) {
|
||||
add_filter(
|
||||
'query_loop_block_query_vars',
|
||||
array( $this, 'get_query_by_attributes' ),
|
||||
|
@ -69,11 +80,10 @@ class ProductQuery extends AbstractBlock {
|
|||
*/
|
||||
public function get_query_by_attributes( $query ) {
|
||||
$parsed_block = $this->parsed_block;
|
||||
if ( ! isset( $parsed_block['attrs']['__woocommerceVariationProps'] ) ) {
|
||||
if ( ! $this->is_woocommerce_variation( $parsed_block ) ) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$variation_props = $parsed_block['attrs']['__woocommerceVariationProps'];
|
||||
$common_query_values = array(
|
||||
'post_type' => 'product',
|
||||
'post_status' => 'publish',
|
||||
|
@ -81,7 +91,7 @@ class ProductQuery extends AbstractBlock {
|
|||
'orderby' => $query['orderby'],
|
||||
'order' => $query['order'],
|
||||
);
|
||||
$on_sale_query = $this->get_on_sale_products_query( $variation_props );
|
||||
$on_sale_query = $this->get_on_sale_products_query( $parsed_block['attrs']['query'] );
|
||||
|
||||
return array_merge( $query, $common_query_values, $on_sale_query );
|
||||
}
|
||||
|
@ -89,11 +99,11 @@ class ProductQuery extends AbstractBlock {
|
|||
/**
|
||||
* Return a query for on sale products.
|
||||
*
|
||||
* @param array $variation_props Dedicated attributes for the variation.
|
||||
* @param array $query_params Block query parameters.
|
||||
* @return array
|
||||
*/
|
||||
private function get_on_sale_products_query( $variation_props ) {
|
||||
if ( ! isset( $variation_props['attributes']['query']['onSale'] ) || true !== $variation_props['attributes']['query']['onSale'] ) {
|
||||
private function get_on_sale_products_query( $query_params ) {
|
||||
if ( ! isset( $query_params['__woocommerceOnSale'] ) || true !== $query_params['__woocommerceOnSale'] ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue