Getting the Product Collection location/context (#43997)
* Early implementation of getting the Product Collection location/context * Solve the problem of async fetch in the hook * Improve typing * Import core data store instead of hardcoding store name * Recognise Product Category and Product Tag * Remove attr property from archive location data * Unify states naming * Add TODO entry * Display the info about the location of Product Collection * Improve the typing * Recognise if Product Collection is nested in Single Product block * Improve cases descriptions and add some defaults to potentially undefined values * Change the taxonomies sourceData * Recognise Mini Cart as Cart context * Recognise attribute as archive contect but no taxonomy * Refactor the function into single useEffect and clean it up * Fix typo * Remove unnecessary import * Stop rendering the output in Editor (it was for demo purposes) * Pass location data to Product Template query in Editor * Replace templateSlugs literal strings with object reference * Rename parseResponse function to more specific name getIdFromResponse * Add dpeendency array to useEffect * Refactor templates detection * Use full taxonomy names instead of shortcuts * Write down scenarios to test * Working scenario * Change the verification way for more robust * Add more robust methods to include Single Product block * Add test Product Collection in Single Product block in a Single Product Template * Imprvoe the order of veryfing the requests * Fix linter issues. Although that makes code less readable * Improve the useGetLocation typing so it's more generic * Rework the E2E tests regarding location of Product Collection and limit their number * Bring back necessary eslint-disable * Remove unused imports * Uncomment line required for other tests * Add changelog * Rename constant from BLOCK_NAME to BLOCK_SLUG as it's a slug * Add a BLOCK_NAME constant and replace the literal block name usages in E2E tests * Fix post merge issues * Fix test after merge * Adjust the tests to kick off waiting for request before action that triggers them
This commit is contained in:
parent
cc0d7368e9
commit
2750e79224
|
@ -14,6 +14,7 @@
|
|||
"queryContext",
|
||||
"displayLayout",
|
||||
"templateSlug",
|
||||
"postId",
|
||||
"queryContextIncludes",
|
||||
"collection"
|
||||
],
|
||||
|
|
|
@ -25,7 +25,7 @@ import type { BlockEditProps, BlockInstance } from '@wordpress/blocks';
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { useProductCollectionQueryContext } from './utils';
|
||||
import { useGetLocation, useProductCollectionQueryContext } from './utils';
|
||||
import './editor.scss';
|
||||
|
||||
const DEFAULT_QUERY_CONTEXT_ATTRIBUTES = [ 'collection', 'id' ];
|
||||
|
@ -125,37 +125,42 @@ const ProductContent = withProduct(
|
|||
}
|
||||
);
|
||||
|
||||
const ProductTemplateEdit = ( {
|
||||
clientId,
|
||||
context: {
|
||||
query: {
|
||||
perPage,
|
||||
offset = 0,
|
||||
order,
|
||||
orderBy,
|
||||
search,
|
||||
exclude,
|
||||
inherit,
|
||||
taxQuery,
|
||||
pages,
|
||||
...restQueryArgs
|
||||
const ProductTemplateEdit = (
|
||||
props: BlockEditProps< {
|
||||
clientId: string;
|
||||
} > & {
|
||||
context: ProductCollectionAttributes;
|
||||
__unstableLayoutClassNames: string;
|
||||
}
|
||||
) => {
|
||||
const {
|
||||
clientId,
|
||||
context: {
|
||||
query: {
|
||||
perPage,
|
||||
offset = 0,
|
||||
order,
|
||||
orderBy,
|
||||
search,
|
||||
exclude,
|
||||
inherit,
|
||||
taxQuery,
|
||||
pages,
|
||||
...restQueryArgs
|
||||
},
|
||||
queryContext = [ { page: 1 } ],
|
||||
templateSlug,
|
||||
displayLayout: { type: layoutType, columns, shrinkColumns } = {
|
||||
type: 'flex',
|
||||
columns: 3,
|
||||
shrinkColumns: false,
|
||||
},
|
||||
queryContextIncludes = [],
|
||||
},
|
||||
queryContext = [ { page: 1 } ],
|
||||
templateSlug,
|
||||
displayLayout: { type: layoutType, columns, shrinkColumns } = {
|
||||
type: 'flex',
|
||||
columns: 3,
|
||||
shrinkColumns: false,
|
||||
},
|
||||
queryContextIncludes = [],
|
||||
},
|
||||
__unstableLayoutClassNames,
|
||||
}: BlockEditProps< {
|
||||
clientId: string;
|
||||
} > & {
|
||||
context: ProductCollectionAttributes;
|
||||
__unstableLayoutClassNames: string;
|
||||
} ) => {
|
||||
__unstableLayoutClassNames,
|
||||
} = props;
|
||||
const location = useGetLocation( props.context, props.clientId );
|
||||
|
||||
const [ { page } ] = queryContext;
|
||||
const [ activeBlockContextId, setActiveBlockContextId ] =
|
||||
useState< string >();
|
||||
|
@ -242,6 +247,7 @@ const ProductTemplateEdit = ( {
|
|||
products: getEntityRecords( 'postType', postType, {
|
||||
...query,
|
||||
...restQueryArgs,
|
||||
location,
|
||||
productCollectionQueryContext,
|
||||
} ),
|
||||
blocks: getBlocks( clientId ),
|
||||
|
@ -261,6 +267,7 @@ const ProductTemplateEdit = ( {
|
|||
templateSlug,
|
||||
taxQuery,
|
||||
restQueryArgs,
|
||||
location,
|
||||
productCollectionQueryContext,
|
||||
loopShopPerPage,
|
||||
]
|
||||
|
|
|
@ -1,8 +1,268 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useMemo } from '@wordpress/element';
|
||||
import { resolveSelect, useSelect } from '@wordpress/data';
|
||||
import { useState, useEffect, useMemo } from '@wordpress/element';
|
||||
import { store as coreStore } from '@wordpress/core-data';
|
||||
import { store as blockEditorStore } from '@wordpress/block-editor';
|
||||
|
||||
type LocationType = 'product' | 'archive' | 'cart' | 'order' | 'generic';
|
||||
type Context< T > = T & {
|
||||
templateSlug?: string;
|
||||
postId?: number;
|
||||
};
|
||||
type SetEntityId = (
|
||||
kind: 'postType' | 'taxonomy',
|
||||
name: 'product' | 'product_cat' | 'product_tag',
|
||||
slug: string,
|
||||
stateSetter: ( entityId: number | null ) => void
|
||||
) => void;
|
||||
|
||||
const templateSlugs = {
|
||||
singleProduct: 'single-product',
|
||||
productCategory: 'taxonomy-product_cat',
|
||||
productTag: 'taxonomy-product_tag',
|
||||
productAttribute: 'taxonomy-product_attribute',
|
||||
orderConfirmation: 'order-confirmation',
|
||||
cart: 'page-cart',
|
||||
checkout: 'page-checkout',
|
||||
};
|
||||
|
||||
const getIdFromResponse = ( resp?: Record< 'id', number >[] ): number | null =>
|
||||
resp && resp.length && resp[ 0 ]?.id ? resp[ 0 ].id : null;
|
||||
|
||||
const setEntityId: SetEntityId = async ( kind, name, slug, stateSetter ) => {
|
||||
const response = ( await resolveSelect( coreStore ).getEntityRecords(
|
||||
kind,
|
||||
name,
|
||||
{
|
||||
_fields: [ 'id' ],
|
||||
slug,
|
||||
}
|
||||
) ) as Record< 'id', number >[];
|
||||
const entityId = getIdFromResponse( response );
|
||||
stateSetter( entityId );
|
||||
};
|
||||
|
||||
const prepareGetEntitySlug =
|
||||
( templateSlug: string ) =>
|
||||
( entitySlug: string ): string =>
|
||||
templateSlug.replace( `${ entitySlug }-`, '' );
|
||||
const prepareIsInSpecificTemplate =
|
||||
( templateSlug: string ) =>
|
||||
( entitySlug: string ): boolean =>
|
||||
templateSlug.includes( entitySlug ) && templateSlug !== entitySlug;
|
||||
const prepareIsInGenericTemplate =
|
||||
( templateSlug: string ) =>
|
||||
( entitySlug: string ): boolean =>
|
||||
templateSlug === entitySlug;
|
||||
|
||||
const createLocationObject = ( type: LocationType, sourceData = {} ) => ( {
|
||||
type,
|
||||
sourceData,
|
||||
} );
|
||||
|
||||
type ContextProperties = {
|
||||
templateSlug: string;
|
||||
postId?: string;
|
||||
};
|
||||
|
||||
export const useGetLocation = < T, >(
|
||||
context: Context< T & ContextProperties >,
|
||||
clientId: string
|
||||
) => {
|
||||
const templateSlug = context.templateSlug || '';
|
||||
const postId = context.postId || null;
|
||||
|
||||
const getEntitySlug = prepareGetEntitySlug( templateSlug );
|
||||
const isInSpecificTemplate = prepareIsInSpecificTemplate( templateSlug );
|
||||
|
||||
// Detect Specific Templates
|
||||
const isInSpecificProductTemplate = isInSpecificTemplate(
|
||||
templateSlugs.singleProduct
|
||||
);
|
||||
const isInSpecificCategoryTemplate = isInSpecificTemplate(
|
||||
templateSlugs.productCategory
|
||||
);
|
||||
const isInSpecificTagTemplate = isInSpecificTemplate(
|
||||
templateSlugs.productTag
|
||||
);
|
||||
|
||||
const [ productId, setProductId ] = useState< number | null >( null );
|
||||
const [ categoryId, setCategoryId ] = useState< number | null >( null );
|
||||
const [ tagId, setTagId ] = useState< number | null >( null );
|
||||
|
||||
useEffect( () => {
|
||||
if ( isInSpecificProductTemplate ) {
|
||||
const slug = getEntitySlug( templateSlugs.singleProduct );
|
||||
setEntityId( 'postType', 'product', slug, setProductId );
|
||||
}
|
||||
|
||||
if ( isInSpecificCategoryTemplate ) {
|
||||
const slug = getEntitySlug( templateSlugs.productCategory );
|
||||
setEntityId( 'taxonomy', 'product_cat', slug, setCategoryId );
|
||||
}
|
||||
|
||||
if ( isInSpecificTagTemplate ) {
|
||||
const slug = getEntitySlug( templateSlugs.productTag );
|
||||
setEntityId( 'taxonomy', 'product_tag', slug, setTagId );
|
||||
}
|
||||
}, [
|
||||
isInSpecificProductTemplate,
|
||||
isInSpecificCategoryTemplate,
|
||||
isInSpecificTagTemplate,
|
||||
getEntitySlug,
|
||||
] );
|
||||
|
||||
const { isInSingleProductBlock, isInMiniCartBlock } = useSelect(
|
||||
( select ) => ( {
|
||||
isInSingleProductBlock:
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore No types for this selector exist yet
|
||||
select( blockEditorStore ).getBlockParentsByBlockName(
|
||||
clientId,
|
||||
'woocommerce/single-product'
|
||||
).length > 0,
|
||||
isInMiniCartBlock:
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore No types for this selector exist yet
|
||||
select( blockEditorStore ).getBlockParentsByBlockName(
|
||||
clientId,
|
||||
'woocommerce/mini-cart-contents'
|
||||
).length > 0,
|
||||
} ),
|
||||
[ clientId ]
|
||||
);
|
||||
|
||||
/**
|
||||
* Case 1.1: SPECIFIC PRODUCT
|
||||
* Single Product block - take product ID from context
|
||||
*/
|
||||
|
||||
if ( isInSingleProductBlock ) {
|
||||
return createLocationObject( 'product', { productId: postId } );
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 1.2: SPECIFIC PRODUCT
|
||||
* Specific Single Product template - take product ID from taxononmy
|
||||
*/
|
||||
|
||||
if ( isInSpecificProductTemplate ) {
|
||||
return createLocationObject( 'product', { productId } );
|
||||
}
|
||||
|
||||
const isInGenericTemplate = prepareIsInGenericTemplate( templateSlug );
|
||||
|
||||
/**
|
||||
* Case 1.3: GENERIC PRODUCT
|
||||
* Generic Single Product template
|
||||
*/
|
||||
|
||||
const isInSingleProductTemplate = isInGenericTemplate(
|
||||
templateSlugs.singleProduct
|
||||
);
|
||||
|
||||
if ( isInSingleProductTemplate ) {
|
||||
return createLocationObject( 'product', { productId: null } );
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 2.1: SPECIFIC TAXONOMY
|
||||
* Specific Category template - take category ID from
|
||||
*/
|
||||
|
||||
if ( isInSpecificCategoryTemplate ) {
|
||||
return createLocationObject( 'archive', {
|
||||
taxonomy: 'product_cat',
|
||||
termId: categoryId,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 2.2: SPECIFIC TAXONOMY
|
||||
* Specific Tag template
|
||||
*/
|
||||
|
||||
if ( isInSpecificTagTemplate ) {
|
||||
return createLocationObject( 'archive', {
|
||||
taxonomy: 'product_tag',
|
||||
termId: tagId,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 2.3: GENERIC TAXONOMY
|
||||
* Generic Taxonomy template
|
||||
*/
|
||||
|
||||
const isInProductsByCategoryTemplate = isInGenericTemplate(
|
||||
templateSlugs.productCategory
|
||||
);
|
||||
|
||||
if ( isInProductsByCategoryTemplate ) {
|
||||
return createLocationObject( 'archive', {
|
||||
taxonomy: 'product_cat',
|
||||
termId: null,
|
||||
} );
|
||||
}
|
||||
|
||||
const isInProductsByTagTemplate = isInGenericTemplate(
|
||||
templateSlugs.productTag
|
||||
);
|
||||
|
||||
if ( isInProductsByTagTemplate ) {
|
||||
return createLocationObject( 'archive', {
|
||||
taxonomy: 'product_tag',
|
||||
termId: null,
|
||||
} );
|
||||
}
|
||||
|
||||
const isInProductsByAttributeTemplate = isInGenericTemplate(
|
||||
templateSlugs.productAttribute
|
||||
);
|
||||
|
||||
if ( isInProductsByAttributeTemplate ) {
|
||||
return createLocationObject( 'archive', {
|
||||
taxonomy: null,
|
||||
termId: null,
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 3: GENERIC CART
|
||||
* Cart/Checkout templates or Mini Cart
|
||||
*/
|
||||
|
||||
const isInCartContext =
|
||||
templateSlug === templateSlugs.cart ||
|
||||
templateSlug === templateSlugs.checkout ||
|
||||
isInMiniCartBlock;
|
||||
|
||||
if ( isInCartContext ) {
|
||||
return createLocationObject( 'cart' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 4: GENERIC ORDER
|
||||
* Order Confirmation template
|
||||
*/
|
||||
|
||||
const isInOrderTemplate = isInGenericTemplate(
|
||||
templateSlugs.orderConfirmation
|
||||
);
|
||||
|
||||
if ( isInOrderTemplate ) {
|
||||
return createLocationObject( 'order' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Case 5: GENERIC
|
||||
* All other cases
|
||||
*/
|
||||
|
||||
return createLocationObject( 'generic' );
|
||||
};
|
||||
|
||||
/**
|
||||
* In Product Collection block, queryContextIncludes attribute contains
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { test as base, expect } from '@woocommerce/e2e-playwright-utils';
|
||||
import type { Request } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -698,16 +699,11 @@ test.describe( 'Product Collection', () => {
|
|||
} );
|
||||
|
||||
test.describe( 'With other blocks', () => {
|
||||
test( 'In Single Product block', async ( {
|
||||
admin,
|
||||
editorUtils,
|
||||
pageObject,
|
||||
} ) => {
|
||||
test( 'In Single Product block', async ( { admin, pageObject } ) => {
|
||||
await admin.createNewPost();
|
||||
await editorUtils.closeWelcomeGuideModal();
|
||||
await pageObject.insertProductCollectionInSingleProductBlock(
|
||||
'featured'
|
||||
);
|
||||
await pageObject.insertProductCollectionInSingleProductBlock();
|
||||
await pageObject.chooseCollectionInPost( 'featured' );
|
||||
await pageObject.refreshLocators( 'editor' );
|
||||
|
||||
const featuredProducts = [
|
||||
'Cap',
|
||||
|
@ -734,6 +730,189 @@ test.describe( 'Product Collection', () => {
|
|||
} );
|
||||
} );
|
||||
|
||||
test.describe( 'Location is recognised', () => {
|
||||
const filterRequest = ( request: Request ) => {
|
||||
const url = request.url();
|
||||
return (
|
||||
url.includes( 'wp/v2/product' ) &&
|
||||
url.includes( 'isProductCollectionBlock=true' )
|
||||
);
|
||||
};
|
||||
|
||||
const filterProductRequest = ( request: Request ) => {
|
||||
const url = request.url();
|
||||
const searchParams = new URLSearchParams( request.url() );
|
||||
|
||||
return (
|
||||
url.includes( 'wp/v2/product' ) &&
|
||||
searchParams.get( 'isProductCollectionBlock' ) === 'true' &&
|
||||
!! searchParams.get( `location[sourceData][productId]` )
|
||||
);
|
||||
};
|
||||
|
||||
const getLocationDetailsFromRequest = (
|
||||
request: Request,
|
||||
locationType?: string
|
||||
) => {
|
||||
const searchParams = new URLSearchParams( request.url() );
|
||||
|
||||
if ( locationType === 'product' ) {
|
||||
return {
|
||||
type: searchParams.get( 'location[type]' ),
|
||||
productId: searchParams.get(
|
||||
`location[sourceData][productId]`
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if ( locationType === 'archive' ) {
|
||||
return {
|
||||
type: searchParams.get( 'location[type]' ),
|
||||
taxonomy: searchParams.get(
|
||||
`location[sourceData][taxonomy]`
|
||||
),
|
||||
termId: searchParams.get( `location[sourceData][termId]` ),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: searchParams.get( 'location[type]' ),
|
||||
sourceData: searchParams.get( `location[sourceData]` ),
|
||||
};
|
||||
};
|
||||
|
||||
test( 'as product in specific Single Product template', async ( {
|
||||
page,
|
||||
pageObject,
|
||||
editorUtils,
|
||||
} ) => {
|
||||
const productName = 'Cap';
|
||||
const productSlug = 'cap';
|
||||
|
||||
await editorUtils.openSpecificProductTemplate(
|
||||
productName,
|
||||
productSlug
|
||||
);
|
||||
|
||||
await editorUtils.insertBlockUsingGlobalInserter(
|
||||
pageObject.BLOCK_NAME
|
||||
);
|
||||
|
||||
const locationReuqestPromise =
|
||||
page.waitForRequest( filterProductRequest );
|
||||
await pageObject.chooseCollectionInTemplate( 'featured' );
|
||||
const locationRequest = await locationReuqestPromise;
|
||||
|
||||
const { type, productId } = getLocationDetailsFromRequest(
|
||||
locationRequest,
|
||||
'product'
|
||||
);
|
||||
|
||||
expect( type ).toBe( 'product' );
|
||||
expect( productId ).toBeTruthy();
|
||||
} );
|
||||
test( 'as category in Products by Category template', async ( {
|
||||
admin,
|
||||
editorUtils,
|
||||
pageObject,
|
||||
page,
|
||||
} ) => {
|
||||
await admin.visitSiteEditor( {
|
||||
postId: `woocommerce/woocommerce//taxonomy-product_cat`,
|
||||
postType: 'wp_template',
|
||||
} );
|
||||
await editorUtils.enterEditMode();
|
||||
await editorUtils.insertBlockUsingGlobalInserter(
|
||||
pageObject.BLOCK_NAME
|
||||
);
|
||||
|
||||
const locationReuqestPromise = page.waitForRequest( filterRequest );
|
||||
await pageObject.chooseCollectionInTemplate( 'featured' );
|
||||
const locationRequest = await locationReuqestPromise;
|
||||
const { type, taxonomy, termId } = getLocationDetailsFromRequest(
|
||||
locationRequest,
|
||||
'archive'
|
||||
);
|
||||
|
||||
expect( type ).toBe( 'archive' );
|
||||
expect( taxonomy ).toBe( 'product_cat' );
|
||||
// Field is sent as a null but browser converts it to empty string
|
||||
expect( termId ).toBe( '' );
|
||||
} );
|
||||
|
||||
test( 'as tag in Products by Tag template', async ( {
|
||||
admin,
|
||||
editorUtils,
|
||||
pageObject,
|
||||
page,
|
||||
} ) => {
|
||||
await admin.visitSiteEditor( {
|
||||
postId: `woocommerce/woocommerce//taxonomy-product_tag`,
|
||||
postType: 'wp_template',
|
||||
} );
|
||||
await editorUtils.enterEditMode();
|
||||
await editorUtils.insertBlockUsingGlobalInserter(
|
||||
pageObject.BLOCK_NAME
|
||||
);
|
||||
|
||||
const locationReuqestPromise = page.waitForRequest( filterRequest );
|
||||
await pageObject.chooseCollectionInTemplate( 'featured' );
|
||||
const locationRequest = await locationReuqestPromise;
|
||||
const { type, taxonomy, termId } = getLocationDetailsFromRequest(
|
||||
locationRequest,
|
||||
'archive'
|
||||
);
|
||||
|
||||
expect( type ).toBe( 'archive' );
|
||||
expect( taxonomy ).toBe( 'product_tag' );
|
||||
// Field is sent as a null but browser converts it to empty string
|
||||
expect( termId ).toBe( '' );
|
||||
} );
|
||||
|
||||
test( 'as generic in post', async ( {
|
||||
admin,
|
||||
editorUtils,
|
||||
pageObject,
|
||||
page,
|
||||
} ) => {
|
||||
await admin.createNewPost();
|
||||
await editorUtils.insertBlockUsingGlobalInserter(
|
||||
pageObject.BLOCK_NAME
|
||||
);
|
||||
|
||||
const locationReuqestPromise = page.waitForRequest( filterRequest );
|
||||
await pageObject.chooseCollectionInPost( 'featured' );
|
||||
const locationRequest = await locationReuqestPromise;
|
||||
const { type, sourceData } =
|
||||
getLocationDetailsFromRequest( locationRequest );
|
||||
|
||||
expect( type ).toBe( 'generic' );
|
||||
// Field is not sent at all. URLSearchParams get method returns a null
|
||||
// if field is not available.
|
||||
expect( sourceData ).toBe( null );
|
||||
} );
|
||||
|
||||
test( 'as product in Single Product block in post', async ( {
|
||||
admin,
|
||||
pageObject,
|
||||
page,
|
||||
} ) => {
|
||||
await admin.createNewPost();
|
||||
await pageObject.insertProductCollectionInSingleProductBlock();
|
||||
const locationReuqestPromise =
|
||||
page.waitForRequest( filterProductRequest );
|
||||
await pageObject.chooseCollectionInPost( 'featured' );
|
||||
const locationRequest = await locationReuqestPromise;
|
||||
const { type, productId } = getLocationDetailsFromRequest(
|
||||
locationRequest,
|
||||
'product'
|
||||
);
|
||||
|
||||
expect( type ).toBe( 'product' );
|
||||
expect( productId ).toBeTruthy();
|
||||
} );
|
||||
} );
|
||||
|
||||
test.describe( 'Query Context in Editor', () => {
|
||||
test( 'Product Catalog: Sends only ID in Query Context', async ( {
|
||||
pageObject,
|
||||
|
|
|
@ -69,12 +69,13 @@ const collectionToButtonNameMap = {
|
|||
};
|
||||
|
||||
class ProductCollectionPage {
|
||||
private BLOCK_NAME = 'woocommerce/product-collection';
|
||||
private BLOCK_SLUG = 'woocommerce/product-collection';
|
||||
private page: Page;
|
||||
private admin: Admin;
|
||||
private editor: Editor;
|
||||
private templateApiUtils: TemplateApiUtils;
|
||||
private editorUtils: EditorUtils;
|
||||
BLOCK_NAME = 'Product Collection (Beta)';
|
||||
productTemplate!: Locator;
|
||||
products!: Locator;
|
||||
productImages!: Locator;
|
||||
|
@ -127,7 +128,7 @@ class ProductCollectionPage {
|
|||
async createNewPostAndInsertBlock( collection?: Collections ) {
|
||||
await this.admin.createNewPost( { legacyCanvas: true } );
|
||||
await this.editor.insertBlock( {
|
||||
name: this.BLOCK_NAME,
|
||||
name: this.BLOCK_SLUG,
|
||||
} );
|
||||
await this.chooseCollectionInPost( collection );
|
||||
await this.refreshLocators( 'editor' );
|
||||
|
@ -142,7 +143,7 @@ class ProductCollectionPage {
|
|||
await this.admin.createNewPost();
|
||||
await this.editorUtils.closeWelcomeGuideModal();
|
||||
await this.editor.insertBlock( {
|
||||
name: this.BLOCK_NAME,
|
||||
name: this.BLOCK_SLUG,
|
||||
} );
|
||||
await this.chooseCollectionInPost( collection );
|
||||
|
||||
|
@ -181,7 +182,7 @@ class ProductCollectionPage {
|
|||
await this.editorUtils.enterEditMode();
|
||||
await this.editorUtils.replaceBlockByBlockName(
|
||||
'core/query',
|
||||
this.BLOCK_NAME
|
||||
this.BLOCK_SLUG
|
||||
);
|
||||
await this.chooseCollectionInTemplate( collection );
|
||||
await this.editor.saveSiteEditorEntities();
|
||||
|
@ -202,7 +203,7 @@ class ProductCollectionPage {
|
|||
} );
|
||||
await this.editorUtils.waitForSiteEditorFinishLoading();
|
||||
await this.editor.canvas.click( 'body' );
|
||||
await this.editor.insertBlock( { name: this.BLOCK_NAME } );
|
||||
await this.editor.insertBlock( { name: this.BLOCK_SLUG } );
|
||||
await this.chooseCollectionInTemplate( collection );
|
||||
await this.editor.openDocumentSettingsSidebar();
|
||||
await this.editor.saveSiteEditorEntities();
|
||||
|
@ -407,7 +408,7 @@ class ProductCollectionPage {
|
|||
async clickDisplaySettings() {
|
||||
// Select the block, so that toolbar is visible.
|
||||
const block = this.page
|
||||
.locator( `[data-type="${ this.BLOCK_NAME }"]` )
|
||||
.locator( `[data-type="${ this.BLOCK_SLUG }"]` )
|
||||
.first();
|
||||
await this.editor.selectBlocks( block );
|
||||
|
||||
|
@ -511,9 +512,7 @@ class ProductCollectionPage {
|
|||
await this.page.setViewportSize( { width, height } );
|
||||
}
|
||||
|
||||
async insertProductCollectionInSingleProductBlock(
|
||||
collection: Collections
|
||||
) {
|
||||
async insertProductCollectionInSingleProductBlock() {
|
||||
this.insertSingleProductBlock();
|
||||
|
||||
const siblingBlock = await this.editorUtils.getBlockByName(
|
||||
|
@ -526,12 +525,10 @@ class ProductCollectionPage {
|
|||
|
||||
await this.editor.selectBlocks( siblingBlock );
|
||||
await this.editorUtils.insertBlock(
|
||||
{ name: this.BLOCK_NAME },
|
||||
{ name: this.BLOCK_SLUG },
|
||||
undefined,
|
||||
parentClientId
|
||||
);
|
||||
await this.chooseCollectionInPost( collection );
|
||||
await this.refreshLocators( 'editor' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -454,4 +454,45 @@ export class EditorUtils {
|
|||
.first()
|
||||
.click();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a specific Single Product template.
|
||||
*/
|
||||
async openSpecificProductTemplate(
|
||||
productName: string,
|
||||
productSlug: string,
|
||||
createIfDoesntExist = true
|
||||
) {
|
||||
await this.page.goto( '/wp-admin/site-editor.php' );
|
||||
await this.page.getByRole( 'button', { name: 'Templates' } ).click();
|
||||
|
||||
const templateButton = this.page.getByRole( 'button', {
|
||||
name: `Product: ${ productName }`,
|
||||
} );
|
||||
|
||||
// Template can be created only once. Go to template if exists,
|
||||
// otherwise create one.
|
||||
if ( await templateButton.isVisible() ) {
|
||||
await templateButton.click();
|
||||
await this.enterEditMode();
|
||||
} else if ( createIfDoesntExist ) {
|
||||
await this.page
|
||||
.getByRole( 'button', { name: 'Add New Template' } )
|
||||
.click();
|
||||
await this.page
|
||||
.getByRole( 'button', { name: 'Single Item: Product' } )
|
||||
.click();
|
||||
await this.page
|
||||
.getByRole( 'option', {
|
||||
name: `${ productName } http://localhost:8889/product/${ productSlug }/`,
|
||||
} )
|
||||
.click();
|
||||
await this.page
|
||||
.getByRole( 'button', {
|
||||
name: 'Skip',
|
||||
} )
|
||||
.click();
|
||||
}
|
||||
await this.closeWelcomeGuideModal();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: add
|
||||
|
||||
Product Collection: recognise the location of block in Editor and pass it with the request
|
Loading…
Reference in New Issue