diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/block.json index a4c8019f379..204b6c942c4 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/block.json @@ -34,6 +34,9 @@ "hideControls": { "default": [], "type": "array" + }, + "queryContextIncludes": { + "type": "array" } }, "providesContext": { @@ -41,6 +44,7 @@ "queryId": "queryId", "query": "query", "displayLayout": "displayLayout", + "queryContextIncludes": "queryContextIncludes", "collection": "collection" }, "supports": { diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/constants.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/constants.ts index e279cecfec8..b63c6f5791b 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/constants.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/constants.ts @@ -71,6 +71,7 @@ export const DEFAULT_ATTRIBUTES: Partial< ProductCollectionAttributes > = { columns: 3, shrinkColumns: true, }, + queryContextIncludes: [ 'collection', 'id' ], }; export const getDefaultQuery = ( diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts index 3b0bb22eb18..07255736244 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-collection/types.ts @@ -19,6 +19,10 @@ export interface ProductCollectionAttributes { convertedFromProducts: boolean; collection?: string; hideControls: FilterName[]; + /** + * Contain the list of attributes that should be included in the queryContext + */ + queryContextIncludes: string[]; } export enum LayoutOptions { diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-template/block.json b/plugins/woocommerce-blocks/assets/js/blocks/product-template/block.json index d9692113747..f334dfe091e 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-template/block.json +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-template/block.json @@ -14,6 +14,7 @@ "queryContext", "displayLayout", "templateSlug", + "queryContextIncludes", "collection" ], "supports": { diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-template/edit.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-template/edit.tsx index 5f4b90b8c71..54527963e8f 100644 --- a/plugins/woocommerce-blocks/assets/js/blocks/product-template/edit.tsx +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-template/edit.tsx @@ -25,8 +25,11 @@ import type { BlockEditProps, BlockInstance } from '@wordpress/blocks'; /** * Internal dependencies */ +import { useProductCollectionQueryContext } from './utils'; import './editor.scss'; +const DEFAULT_QUERY_CONTEXT_ATTRIBUTES = [ 'collection', 'id' ]; + const ProductTemplateInnerBlocks = () => { const innerBlocksProps = useInnerBlocksProps( { className: 'wc-block-product' }, @@ -144,6 +147,7 @@ const ProductTemplateEdit = ( { columns: 3, shrinkColumns: false, }, + queryContextIncludes = [], }, __unstableLayoutClassNames, }: BlockEditProps< { @@ -161,6 +165,19 @@ const ProductTemplateEdit = ( { 12, isNumber ); + + // Add default query context attributes to queryContextIncludes + const queryContextIncludesWithDefaults = [ + ...new Set( + queryContextIncludes.concat( DEFAULT_QUERY_CONTEXT_ATTRIBUTES ) + ), + ]; + + const productCollectionQueryContext = useProductCollectionQueryContext( { + clientId, + queryContextIncludes: queryContextIncludesWithDefaults, + } ); + const { products, blocks } = useSelect( ( select ) => { const { getEntityRecords, getTaxonomies } = select( coreStore ); @@ -225,6 +242,7 @@ const ProductTemplateEdit = ( { products: getEntityRecords( 'postType', postType, { ...query, ...restQueryArgs, + productCollectionQueryContext, } ), blocks: getBlocks( clientId ), }; @@ -243,6 +261,8 @@ const ProductTemplateEdit = ( { templateSlug, taxQuery, restQueryArgs, + productCollectionQueryContext, + loopShopPerPage, ] ); const blockContexts = useMemo( diff --git a/plugins/woocommerce-blocks/assets/js/blocks/product-template/utils.tsx b/plugins/woocommerce-blocks/assets/js/blocks/product-template/utils.tsx new file mode 100644 index 00000000000..33150c41266 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/blocks/product-template/utils.tsx @@ -0,0 +1,80 @@ +/** + * External dependencies + */ +import { useSelect } from '@wordpress/data'; +import { useMemo } from '@wordpress/element'; + +/** + * In Product Collection block, queryContextIncludes attribute contains + * list of attribute names that should be included in the query context. + * + * This hook returns the query context object based on the attribute names + * provided in the queryContextIncludes array. + * + * Example: + * { + * clientID = 'd2c7e34f-70d6-417c-b582-f554a3a575f3', + * queryContextIncludes = [ 'collection' ] + * } + * + * The hook will return the following query context object: + * { + * collection: 'woocommerce/product-collection/featured' + * } + * + * @param args Arguments for the hook. + * @param args.clientId Client ID of the inner block. + * @param args.queryContextIncludes Array of attribute names to be included in the query context. + * + * @return Query context object. + */ +export const useProductCollectionQueryContext = ( { + clientId, + queryContextIncludes, +}: { + clientId: string; + queryContextIncludes: string[]; +} ) => { + const productCollectionBlockAttributes = useSelect( + ( select ) => { + const { getBlockParentsByBlockName, getBlockAttributes } = + select( 'core/block-editor' ); + + const parentBlocksClientIds = getBlockParentsByBlockName( + clientId, + 'woocommerce/product-collection', + true + ); + + if ( parentBlocksClientIds?.length ) { + const closestParentClientId = parentBlocksClientIds[ 0 ]; + return getBlockAttributes( closestParentClientId ); + } + + return null; + }, + [ clientId ] + ); + + return useMemo( () => { + // If the product collection block is not found, return null. + if ( ! productCollectionBlockAttributes ) { + return null; + } + + const queryContext: { + [ key: string ]: unknown; + } = {}; + + if ( queryContextIncludes?.length ) { + queryContextIncludes.forEach( ( attribute: string ) => { + if ( productCollectionBlockAttributes?.[ attribute ] ) { + queryContext[ attribute ] = + productCollectionBlockAttributes[ attribute ]; + } + } ); + } + + return queryContext; + }, [ queryContextIncludes, productCollectionBlockAttributes ] ); +}; diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts index b5412ca8c4f..1b15de66d8d 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.block_theme.spec.ts @@ -733,4 +733,42 @@ test.describe( 'Product Collection', () => { ); } ); } ); + + test.describe( 'Query Context in Editor', () => { + test( 'Product Catalog: Sends only ID in Query Context', async ( { + pageObject, + } ) => { + const url = await pageObject.setupAndFetchQueryContextURL( { + collection: 'productCatalog', + } ); + + await expect( + url.searchParams.has( 'productCollectionQueryContext[id]' ) + ).toBeTruthy(); + + // There shouldn't be collection in the query context + // Because Product Catalog isn't a collection + await expect( + url.searchParams.has( + 'productCollectionQueryContext[collection]' + ) + ).toBeFalsy(); + } ); + + test( 'Collections: collection should be present in query context', async ( { + pageObject, + } ) => { + const url = await pageObject.setupAndFetchQueryContextURL( { + collection: 'onSale', + } ); + + const collectionName = url.searchParams.get( + 'productCollectionQueryContext[collection]' + ); + await expect( collectionName ).toBeTruthy(); + await expect( collectionName ).toBe( + 'woocommerce/product-collection/on-sale' + ); + } ); + } ); } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts index 730119a3849..7c4b5d264af 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/product-collection/product-collection.page.ts @@ -134,6 +134,32 @@ class ProductCollectionPage { await this.editor.openDocumentSettingsSidebar(); } + async setupAndFetchQueryContextURL( { + collection, + }: { + collection: Collections; + } ) { + await this.admin.createNewPost(); + await this.editorUtils.closeWelcomeGuideModal(); + await this.editor.insertBlock( { + name: this.BLOCK_NAME, + } ); + await this.chooseCollectionInPost( collection ); + + // Wait for response with productCollectionQueryContext query parameter. + const WP_PRODUCT_ENDPOINT = '/wp/v2/product'; + const QUERY_CONTEXT_PARAM = 'productCollectionQueryContext'; + const response = await this.page.waitForResponse( + ( currentResponse ) => + currentResponse.url().includes( WP_PRODUCT_ENDPOINT ) && + currentResponse.url().includes( QUERY_CONTEXT_PARAM ) && + currentResponse.status() === 200 + ); + + const url = new URL( response.url() ); + return url; + } + async publishAndGoToFrontend() { await this.editor.publishPost(); const url = new URL( this.page.url() ); @@ -155,7 +181,7 @@ class ProductCollectionPage { await this.editorUtils.enterEditMode(); await this.editorUtils.replaceBlockByBlockName( 'core/query', - 'woocommerce/product-collection' + this.BLOCK_NAME ); await this.chooseCollectionInTemplate( collection ); await this.editor.saveSiteEditorEntities(); @@ -500,7 +526,7 @@ class ProductCollectionPage { await this.editor.selectBlocks( siblingBlock ); await this.editorUtils.insertBlock( - { name: 'woocommerce/product-collection' }, + { name: this.BLOCK_NAME }, undefined, parentClientId ); diff --git a/plugins/woocommerce/changelog/44150-try-query-context-block-params b/plugins/woocommerce/changelog/44150-try-query-context-block-params new file mode 100644 index 00000000000..2adf63e31dd --- /dev/null +++ b/plugins/woocommerce/changelog/44150-try-query-context-block-params @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Add `queryContextIncludes` attribute to Product Collection block \ No newline at end of file