This repository has been archived by the owner on Feb 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 217
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Product Query Block POC (Phase 1) (#6812)
* Move `EditorBlock` to general `type-defs` `EditorBlock` was scoped under the `featured-items` directory at the time of its creation. It is, however, a useful type that should be shared repo-wide. For this reason, I am moving it into the `blocks` type-defs and updating all the references. * Define types for the Product Query block Also defines a more generic `WooCommerceBlockVariation` type which should be also useful in the future to implement a similar pattern. * Add Product Query utils Add two utility functions: 1. `isWooQueryBlockVariation`: is used to check whether a given block is a variation of the core Query Loop block, and also one of the allowed variations within our repo. See: `QueryVariation` enum type. 2. `setCustomQueryAttribute`: is a shorthand to set an attribute within the variation query attribute. * Refactor and cleanup the JS demo code Specifically: 1. Creates a `constant.ts` file to store all shared constants. Currently, the default variation attributes. 2. Move the variations to their own directory. One file per variation. 3. Move the inspector controls into own file and create a conditional logic to allow showing only certain settings. * Update webpack config * Add ProductQuery class * Fix `QueryVariation` enum We had changed the Products on Sale variation slug to something else, but we had forgotten to update the proper enum. * Remove unused params from `update_query` The filter we added to Gutenberg will pass the block and the page, as we might need them in the future and we want to minimize the amount of changes we'll have to do upstream. However, we currently do not use those, so I removed them from our own inner function. Co-authored-by: Lucio Giannotta <[email protected]>
- Loading branch information
1 parent
986b3cf
commit 7757b85
Showing
12 changed files
with
467 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { QueryBlockQuery } from './types'; | ||
|
||
export const QUERY_DEFAULT_ATTRIBUTES: { query: QueryBlockQuery } = { | ||
query: { | ||
perPage: 6, | ||
pages: 0, | ||
offset: 0, | ||
postType: 'product', | ||
order: 'desc', | ||
orderBy: 'date', | ||
author: '', | ||
search: '', | ||
exclude: [], | ||
sticky: '', | ||
inherit: false, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { Block } from '@wordpress/blocks'; | ||
import { addFilter } from '@wordpress/hooks'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import './inspector-controls'; | ||
import './variations/product-query'; | ||
import './variations/products-on-sale'; | ||
|
||
function registerProductQueryVariationAttributes( | ||
props: Block, | ||
blockName: string | ||
) { | ||
if ( blockName === 'core/query' ) { | ||
// Gracefully handle if settings.attributes is undefined. | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore -- We need this because `attributes` is marked as `readonly` | ||
props.attributes = { | ||
...props.attributes, | ||
__woocommerceVariationProps: { | ||
type: 'object', | ||
}, | ||
}; | ||
} | ||
return props; | ||
} | ||
|
||
addFilter( | ||
'blocks.registerBlockType', | ||
'core/custom-class-name/attribute', | ||
registerProductQueryVariationAttributes | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { InspectorControls } from '@wordpress/block-editor'; | ||
import { ToggleControl } from '@wordpress/components'; | ||
import { addFilter } from '@wordpress/hooks'; | ||
import { EditorBlock } from '@woocommerce/types'; | ||
import { ElementType } from 'react'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { ProductQueryBlock } from './types'; | ||
import { isWooQueryBlockVariation, setCustomQueryAttribute } from './utils'; | ||
|
||
export const INSPECTOR_CONTROLS = { | ||
onSale: ( props: ProductQueryBlock ) => ( | ||
<ToggleControl | ||
label={ __( | ||
'Show only products on sale', | ||
'woo-gutenberg-products-block' | ||
) } | ||
checked={ | ||
props.attributes.__woocommerceVariationProps?.attributes?.query | ||
?.onSale || false | ||
} | ||
onChange={ ( onSale ) => { | ||
setCustomQueryAttribute( props, { onSale } ); | ||
} } | ||
/> | ||
), | ||
}; | ||
|
||
export const withProductQueryControls = | ||
< T extends EditorBlock< T > >( BlockEdit: ElementType ) => | ||
( props: ProductQueryBlock ) => { | ||
return isWooQueryBlockVariation( props ) ? ( | ||
<> | ||
<BlockEdit { ...props } /> | ||
<InspectorControls> | ||
{ Object.entries( INSPECTOR_CONTROLS ).map( | ||
( [ key, Control ] ) => | ||
props.attributes.__woocommerceVariationProps.attributes?.disabledInspectorControls?.includes( | ||
key | ||
) ? null : ( | ||
<Control { ...props } /> | ||
) | ||
) } | ||
</InspectorControls> | ||
</> | ||
) : ( | ||
<BlockEdit { ...props } /> | ||
); | ||
}; | ||
|
||
addFilter( 'editor.BlockEdit', 'core/query', withProductQueryControls ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { BlockInstance } from '@wordpress/blocks'; | ||
import type { EditorBlock } from '@woocommerce/types'; | ||
|
||
export interface ProductQueryArguments { | ||
/** | ||
* Display only products on sale. | ||
* | ||
* Will generate the following `meta_query`: | ||
* | ||
* ``` | ||
* array( | ||
* 'relation' => 'OR', | ||
* array( // Simple products type | ||
* 'key' => '_sale_price', | ||
* 'value' => 0, | ||
* 'compare' => '>', | ||
* 'type' => 'numeric', | ||
* ), | ||
* array( // Variable products type | ||
* 'key' => '_min_variation_sale_price', | ||
* 'value' => 0, | ||
* 'compare' => '>', | ||
* 'type' => 'numeric', | ||
* ), | ||
* ) | ||
* ``` | ||
*/ | ||
onSale?: boolean; | ||
} | ||
|
||
export type ProductQueryBlock = | ||
WooCommerceBlockVariation< ProductQueryAttributes >; | ||
|
||
export interface ProductQueryAttributes { | ||
/** | ||
* An array of controls to disable in the inspector. | ||
* | ||
* @example `[ 'stockStatus' ]` will not render the dropdown for stock status. | ||
*/ | ||
disabledInspectorControls?: string[]; | ||
/** | ||
* Query attributes that define which products will be fetched. | ||
*/ | ||
query?: ProductQueryArguments; | ||
} | ||
|
||
export interface QueryBlockQuery { | ||
author?: string; | ||
exclude?: string[]; | ||
inherit: boolean; | ||
offset?: number; | ||
order: 'asc' | 'desc'; | ||
orderBy: 'date' | 'relevance'; | ||
pages?: number; | ||
parents?: number[]; | ||
perPage?: number; | ||
postType: string; | ||
search?: string; | ||
sticky?: string; | ||
taxQuery?: string; | ||
} | ||
|
||
export enum QueryVariation { | ||
/** The main, fully customizable, Product Query block */ | ||
PRODUCT_QUERY = 'product-query', | ||
/** 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 > >; | ||
} >; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import { | ||
ProductQueryArguments, | ||
ProductQueryBlock, | ||
QueryVariation, | ||
} from './types'; | ||
|
||
/** | ||
* Identifies if a block is a Query block variation from our conventions | ||
* | ||
* We are extending Gutenberg's core Query block with our variations, and | ||
* also adding extra namespaced attributes. If those namespaced attributes | ||
* are present, we can be fairly sure it is our own registered variation. | ||
*/ | ||
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 | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* Sets the new query arguments of a Product Query block | ||
* | ||
* Because we add a new set of deeply nested attributes to the query | ||
* block, this utility function makes it easier to change just the | ||
* options relating to our custom query, while keeping the code | ||
* clean. | ||
*/ | ||
export function setCustomQueryAttribute( | ||
block: ProductQueryBlock, | ||
attributes: Partial< ProductQueryArguments > | ||
) { | ||
const { __woocommerceVariationProps } = block.attributes; | ||
|
||
block.setAttributes( { | ||
__woocommerceVariationProps: { | ||
...__woocommerceVariationProps, | ||
attributes: { | ||
...__woocommerceVariationProps.attributes, | ||
query: { | ||
...__woocommerceVariationProps.attributes?.query, | ||
...attributes, | ||
}, | ||
}, | ||
}, | ||
} ); | ||
} |
50 changes: 50 additions & 0 deletions
50
assets/js/blocks/product-query/variations/product-query.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { isExperimentalBuild } from '@woocommerce/block-settings'; | ||
import { registerBlockVariation } from '@wordpress/blocks'; | ||
import { Icon } from '@wordpress/components'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { sparkles } from '@wordpress/icons'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { QUERY_DEFAULT_ATTRIBUTES } from '../constants'; | ||
|
||
if ( isExperimentalBuild() ) { | ||
registerBlockVariation( 'core/query', { | ||
name: 'woocommerce/product-query', | ||
title: __( 'Product Query', 'woo-gutenberg-products-block' ), | ||
isActive: ( attributes ) => { | ||
return ( | ||
attributes?.__woocommerceVariationProps?.name === | ||
'product-query' | ||
); | ||
}, | ||
icon: { | ||
src: ( | ||
<Icon | ||
icon={ sparkles } | ||
className="wc-block-editor-components-block-icon wc-block-editor-components-block-icon--sparkles" | ||
/> | ||
), | ||
}, | ||
attributes: { | ||
...QUERY_DEFAULT_ATTRIBUTES, | ||
__woocommerceVariationProps: { | ||
name: 'product-query', | ||
}, | ||
}, | ||
innerBlocks: [ | ||
[ | ||
'core/post-template', | ||
{}, | ||
[ [ 'core/post-title' ], [ 'core/post-featured-image' ] ], | ||
], | ||
[ 'core/query-pagination' ], | ||
[ 'core/query-no-results' ], | ||
], | ||
scope: [ 'block', 'inserter' ], | ||
} ); | ||
} |
53 changes: 53 additions & 0 deletions
53
assets/js/blocks/product-query/variations/products-on-sale.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { isExperimentalBuild } from '@woocommerce/block-settings'; | ||
import { registerBlockVariation } from '@wordpress/blocks'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { Icon, percent } from '@wordpress/icons'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { QUERY_DEFAULT_ATTRIBUTES } from '../constants'; | ||
|
||
if ( isExperimentalBuild() ) { | ||
registerBlockVariation( 'core/query', { | ||
name: 'woocommerce/query-products-on-sale', | ||
title: __( 'Products on Sale', 'woo-gutenberg-products-block' ), | ||
isActive: ( blockAttributes ) => | ||
blockAttributes?.__woocommerceVariationProps?.name === | ||
'query-products-on-sale' || | ||
blockAttributes?.__woocommerceVariationProps?.query?.onSale === | ||
true, | ||
icon: { | ||
src: ( | ||
<Icon | ||
icon={ percent } | ||
className="wc-block-editor-components-block-icon wc-block-editor-components-block-icon--percent" | ||
/> | ||
), | ||
}, | ||
attributes: { | ||
...QUERY_DEFAULT_ATTRIBUTES, | ||
__woocommerceVariationProps: { | ||
name: 'query-products-on-sale', | ||
attributes: { | ||
query: { | ||
onSale: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
innerBlocks: [ | ||
[ | ||
'core/post-template', | ||
{}, | ||
[ [ 'core/post-title' ], [ 'core/post-featured-image' ] ], | ||
], | ||
[ 'core/query-pagination' ], | ||
[ 'core/query-no-results' ], | ||
], | ||
scope: [ 'block', 'inserter' ], | ||
} ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.