Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Refactor Product Query to use the latest Gutenberg APIs
Browse files Browse the repository at this point in the history
As we worked with Gutenberg folks in WordPress/gutenberg#43590,
WordPress/gutenberg#/43632 and WordPress/gutenberg#/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.
  • Loading branch information
sunyatasattva committed Sep 16, 2022
1 parent 060f63c commit ff85d47
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 70 deletions.
28 changes: 16 additions & 12 deletions assets/js/blocks/product-query/constants.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -39,7 +43,7 @@ export const INNER_BLOCKS_TEMPLATE: InnerBlockTemplate[] = [
'core/post-template',
{},
[
[ 'woocommerce/product-image', undefined, [] ],
[ 'woocommerce/product-image' ],
[
'core/post-title',
{
Expand All @@ -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' ],
];
22 changes: 11 additions & 11 deletions assets/js/blocks/product-query/inspector-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) => (
Expand All @@ -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 } );
} }
/>
),
Expand All @@ -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>
</>
Expand Down
28 changes: 16 additions & 12 deletions assets/js/blocks/product-query/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/**
* External dependencies
*/
import { BlockInstance } from '@wordpress/blocks';
import type { EditorBlock } from '@woocommerce/types';

export interface ProductQueryArguments {
Expand All @@ -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 {
/**
Expand All @@ -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[];
Expand All @@ -69,11 +81,3 @@ export enum QueryVariation {
/** Only shows products on sale */
PRODUCTS_ON_SALE = '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 > >;
} >;
50 changes: 39 additions & 11 deletions assets/js/blocks/product-query/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* External dependencies
*/
import { useSelect } from '@wordpress/data';
import { store as WP_BLOCKS_STORE } from '@wordpress/blocks';

/**
* Internal dependencies
*/
Expand All @@ -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
*
Expand Down Expand Up @@ -35,20 +48,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 ]
);
}
27 changes: 16 additions & 11 deletions assets/js/blocks/product-query/variations/product-query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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' ],
} );
Expand Down
37 changes: 24 additions & 13 deletions assets/js/blocks/product-query/variations/products-on-sale.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,15 +36,20 @@ if ( isExperimentalBuild() ) {
},
attributes: {
...QUERY_DEFAULT_ATTRIBUTES,
__woocommerceVariationProps: {
name: 'query-products-on-sale',
attributes: {
query: {
onSale: true,
},
},
namespace: VARIATION_NAME,
query: {
...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' ],
} );
Expand Down

0 comments on commit ff85d47

Please sign in to comment.