Fix incorrect product data displayed in Product Collection in context of Single Product block (#44123)

* Initial approach to fix incorrect product data displayed in Product Collection in context of Single Product block

* Remove the BlockCOntextProvider

* Add Block context for title and summary

* Add changelog

* Improve typing

* Add E2E test with Product Collection inside Single Product block

* Improve insertBLock function description

* Make productPrices selector in Product Collectionmore specific

* Extract components props into a types
This commit is contained in:
Karol Manijak 2024-02-01 17:55:38 +01:00 committed by GitHub
parent 402b9bf096
commit 84f2de633d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 148 additions and 30 deletions

View File

@ -15,10 +15,12 @@ import {
} from '@wordpress/block-editor';
import { Spinner } from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';
import type { BlockEditProps } from '@wordpress/blocks';
import { ProductCollectionAttributes } from '@woocommerce/blocks/product-collection/types';
import { getSettingWithCoercion } from '@woocommerce/settings';
import { isNumber } from '@woocommerce/types';
import { isNumber, ProductResponseItem } from '@woocommerce/types';
import { ProductDataContextProvider } from '@woocommerce/shared-context';
import { withProduct } from '@woocommerce/block-hocs';
import type { BlockEditProps, BlockInstance } from '@wordpress/blocks';
const ProductTemplateInnerBlocks = () => {
const innerBlocksProps = useInnerBlocksProps(
@ -28,17 +30,19 @@ const ProductTemplateInnerBlocks = () => {
return <li { ...innerBlocksProps } />;
};
type ProductTemplateBlockPreviewProps = {
blocks: object[];
blockContextId: string;
isHidden: boolean;
setActiveBlockContextId: ( blockContextId: string ) => void;
};
const ProductTemplateBlockPreview = ( {
blocks,
blockContextId,
isHidden,
setActiveBlockContextId,
}: {
blocks: object[];
blockContextId: string;
isHidden: boolean;
setActiveBlockContextId: ( blockContextId: string ) => void;
} ) => {
}: ProductTemplateBlockPreviewProps ) => {
const blockPreviewProps = useBlockPreview( {
blocks,
props: {
@ -69,6 +73,50 @@ const ProductTemplateBlockPreview = ( {
const MemoizedProductTemplateBlockPreview = memo( ProductTemplateBlockPreview );
type ProductContentProps = {
attributes: { productId: string };
isLoading: boolean;
product: ProductResponseItem;
displayTemplate: boolean;
blocks: BlockInstance[];
blockContext: {
postType: string;
postId: string;
};
setActiveBlockContextId: ( id: string ) => void;
};
const ProductContent = withProduct(
( {
isLoading,
product,
displayTemplate,
blocks,
blockContext,
setActiveBlockContextId,
}: ProductContentProps ) => {
return (
<BlockContextProvider
key={ blockContext.postId }
value={ blockContext }
>
<ProductDataContextProvider
product={ product }
isLoading={ isLoading }
>
{ displayTemplate ? <ProductTemplateInnerBlocks /> : null }
<MemoizedProductTemplateBlockPreview
blocks={ blocks }
blockContextId={ blockContext.postId }
setActiveBlockContextId={ setActiveBlockContextId }
isHidden={ displayTemplate }
/>
</ProductDataContextProvider>
</BlockContextProvider>
);
}
);
const ProductTemplateEdit = ( {
clientId,
context: {
@ -100,7 +148,8 @@ const ProductTemplateEdit = ( {
__unstableLayoutClassNames: string;
} ) => {
const [ { page } ] = queryContext;
const [ activeBlockContextId, setActiveBlockContextId ] = useState();
const [ activeBlockContextId, setActiveBlockContextId ] =
useState< string >();
const postType = 'product';
const loopShopPerPage = getSettingWithCoercion(
'loopShopPerPage',
@ -241,28 +290,26 @@ const ProductTemplateEdit = ( {
return (
<ul { ...blockProps }>
{ blockContexts &&
blockContexts.map( ( blockContext ) => (
<BlockContextProvider
key={ blockContext.postId }
value={ blockContext }
>
{ blockContext.postId ===
( activeBlockContextId ||
blockContexts[ 0 ]?.postId ) ? (
<ProductTemplateInnerBlocks />
) : null }
<MemoizedProductTemplateBlockPreview
blocks={ blocks }
blockContextId={ blockContext.postId }
setActiveBlockContextId={ setActiveBlockContextId }
isHidden={
blockContexts.map( ( blockContext ) => {
const displayTemplate =
blockContext.postId ===
( activeBlockContextId ||
blockContexts[ 0 ]?.postId )
}
( activeBlockContextId || blockContexts[ 0 ]?.postId );
return (
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore isLoading and product props are missing as they're coming from untyped withProduct HOC.
<ProductContent
key={ blockContext.postId }
attributes={ {
productId: blockContext.postId,
} }
blocks={ blocks }
displayTemplate={ displayTemplate }
blockContext={ blockContext }
setActiveBlockContextId={ setActiveBlockContextId }
/>
</BlockContextProvider>
) ) }
);
} ) }
</ul>
);
};

View File

@ -674,4 +674,71 @@ test.describe( 'Product Collection', () => {
} );
} );
} );
test.describe( 'With other blocks', () => {
test( 'In Single Product block', async ( {
admin,
editor,
page,
editorUtils,
pageObject,
} ) => {
await admin.createNewPost();
await editorUtils.closeWelcomeGuideModal();
// Prepare Single Product block
await editor.insertBlock( { name: 'woocommerce/single-product' } );
await page.waitForResponse(
( response ) =>
response.url().includes( 'wc/store/v1/products' ) &&
response.status() === 200
);
const singleProductBlock = await editorUtils.getBlockByName(
'woocommerce/single-product'
);
await singleProductBlock
.locator( 'input[type="radio"]' )
.nth( 0 )
.click();
await singleProductBlock.getByText( 'Done' ).click();
await page.getByLabel( 'Block: Product Title' ).press( 'Enter' );
await page
.getByLabel(
'Empty block; start writing or type forward slash to choose a block'
)
.pressSequentially( '/Product Collection (Beta)' );
await page
.getByRole( 'option', {
name: 'Product Collection (Beta)',
exact: true,
} )
.first()
.click();
await pageObject.chooseCollectionInPost( 'featured' );
const featuredProducts = [
'Cap',
'Hoodie with Zipper',
'Sunglasses',
'V-Neck T-Shirt',
];
const featuredProductsPrices = [
'Previous price:$18.00Discounted price:$16.00',
'$45.00',
'$90.00',
'Price between $15.00 and $20.00$15.00 — $20.00',
];
await expect( pageObject.products ).toHaveCount( 4 );
// This verifies if Core's block context is provided
await expect( pageObject.productTitles ).toHaveText(
featuredProducts
);
// This verifies if Blocks's product context is provided
await expect( pageObject.productPrices ).toHaveText(
featuredProductsPrices
);
} );
} );
} );

View File

@ -526,7 +526,7 @@ class ProductCollectionPage {
this.productTitles = this.productTemplate
.locator( SELECTORS.productTitle )
.locator( 'visible=true' );
this.productPrices = this.page
this.productPrices = this.productTemplate
.locator( SELECTORS.productPrice.inEditor )
.locator( 'visible=true' );
this.addToCartButtons = this.page

View File

@ -47,7 +47,7 @@ export class EditorUtils {
// todo: Make a PR to @wordpress/e2e-test-utils-playwright to add this method.
/**
* Inserts a block after a given client ID.
* Inserts a block inside a given client ID.
*
*/
async insertBlock(

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Product Collection: fix incorrect product data being displayed when Product Collection is placed in Single Product block