diff --git a/assets/js/blocks/product-query/constants.ts b/assets/js/blocks/product-query/constants.ts
index 1c05f1276..0ae12ff26 100644
--- a/assets/js/blocks/product-query/constants.ts
+++ b/assets/js/blocks/product-query/constants.ts
@@ -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' ],
];
diff --git a/assets/js/blocks/product-query/inspector-controls.tsx b/assets/js/blocks/product-query/inspector-controls.tsx
index a3e2768b0..56cfc2410 100644
--- a/assets/js/blocks/product-query/inspector-controls.tsx
+++ b/assets/js/blocks/product-query/inspector-controls.tsx
@@ -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 ) ? (
<>
{ Object.entries( INSPECTOR_CONTROLS ).map(
( [ key, Control ] ) =>
- props.attributes.__woocommerceVariationProps.attributes?.disabledInspectorControls?.includes(
- key
- ) ? null : (
+ allowedControls?.includes( key ) ? (
- )
+ ) : null
) }
>
diff --git a/assets/js/blocks/product-query/types.ts b/assets/js/blocks/product-query/types.ts
index 3329005a3..f311a3afd 100644
--- a/assets/js/blocks/product-query/types.ts
+++ b/assets/js/blocks/product-query/types.ts
@@ -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 > >;
-} >;
diff --git a/assets/js/blocks/product-query/utils.tsx b/assets/js/blocks/product-query/utils.tsx
index 61be85d5e..3fbade0fa 100644
--- a/assets/js/blocks/product-query/utils.tsx
+++ b/assets/js/blocks/product-query/utils.tsx
@@ -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: {
+ ...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 ]
+ );
+}
diff --git a/assets/js/blocks/product-query/variations/product-query.tsx b/assets/js/blocks/product-query/variations/product-query.tsx
index 3a53dbd9a..83c46240e 100644
--- a/assets/js/blocks/product-query/variations/product-query.tsx
+++ b/assets/js/blocks/product-query/variations/product-query.tsx
@@ -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: (
- blockAttributes?.__woocommerceVariationProps?.name ===
- 'query-products-on-sale' ||
- blockAttributes?.__woocommerceVariationProps?.query?.onSale ===
- true,
+ blockAttributes.namespace === VARIATION_NAME ||
+ blockAttributes.query?.__woocommerceOnSale === true,
icon: {
src: (
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 @@ public function update_query( $pre_render, $parsed_block ) {
*/
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 @@ public function get_query_by_attributes( $query ) {
'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 @@ public function get_query_by_attributes( $query ) {
/**
* 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();
}