Product Collection: Don't render when empty unless the no results block is present (#50404)
* Disable PC render when content empty unless no results block is present * Introduce empty PC classname * Add E2E test * Add changelog entry * Ensure the render is preserved as expected * Do not render empty div * Update tests * Try a different logic * Add hint to the docblock * Remove obsolete e2e test * Update render handler name to acknowledge context * Fix tests failing due to recent v3 update * Restore block interactivity --------- Co-authored-by: Manish Menaria <the.manish.menaria@gmail.com>
This commit is contained in:
parent
062c4ed76a
commit
ff197207ef
|
@ -81,6 +81,87 @@ test.describe( 'Product Collection', () => {
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
test.describe( 'when no results are found', () => {
|
||||||
|
test.beforeEach( async ( { admin } ) => {
|
||||||
|
await admin.createNewPost();
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( 'does not render', async ( { page, editor, pageObject } ) => {
|
||||||
|
await pageObject.insertProductCollection();
|
||||||
|
await pageObject.chooseCollectionInPost( 'featured' );
|
||||||
|
await pageObject.addFilter( 'Price Range' );
|
||||||
|
await pageObject.setPriceRange( {
|
||||||
|
max: '1',
|
||||||
|
} );
|
||||||
|
|
||||||
|
const featuredBlock = editor.canvas.getByLabel( 'Block: Featured' );
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
featuredBlock.getByText( 'Featured products' )
|
||||||
|
).toBeVisible();
|
||||||
|
// The "No results found" info is rendered in editor for all collections.
|
||||||
|
await expect(
|
||||||
|
featuredBlock.getByText( 'No results found' )
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await pageObject.publishAndGoToFrontend();
|
||||||
|
|
||||||
|
const content = page.locator( 'main' );
|
||||||
|
|
||||||
|
await expect( content ).not.toContainText( 'Featured products' );
|
||||||
|
await expect( content ).not.toContainText( 'No results found' );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// This test ensures the runtime render state is correctly reset for
|
||||||
|
// each block.
|
||||||
|
test( 'does not prevent subsequent blocks from render', async ( {
|
||||||
|
page,
|
||||||
|
pageObject,
|
||||||
|
} ) => {
|
||||||
|
await pageObject.insertProductCollection();
|
||||||
|
await pageObject.chooseCollectionInPost( 'featured' );
|
||||||
|
await pageObject.addFilter( 'Price Range' );
|
||||||
|
await pageObject.setPriceRange( {
|
||||||
|
max: '1',
|
||||||
|
} );
|
||||||
|
|
||||||
|
await pageObject.insertProductCollection();
|
||||||
|
await pageObject.chooseCollectionInPost( 'topRated' );
|
||||||
|
|
||||||
|
await pageObject.refreshLocators( 'editor' );
|
||||||
|
await expect( pageObject.products ).toHaveCount( 5 );
|
||||||
|
|
||||||
|
await pageObject.publishAndGoToFrontend();
|
||||||
|
|
||||||
|
await pageObject.refreshLocators( 'frontend' );
|
||||||
|
await expect( pageObject.products ).toHaveCount( 5 );
|
||||||
|
await expect( page.locator( 'main' ) ).not.toContainText(
|
||||||
|
'Featured products'
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
|
||||||
|
test( 'renders if No Results block is present', async ( {
|
||||||
|
page,
|
||||||
|
editor,
|
||||||
|
pageObject,
|
||||||
|
} ) => {
|
||||||
|
await pageObject.insertProductCollection();
|
||||||
|
await pageObject.chooseCollectionInPost( 'productCatalog' );
|
||||||
|
await pageObject.addFilter( 'Price Range' );
|
||||||
|
await pageObject.setPriceRange( {
|
||||||
|
max: '1',
|
||||||
|
} );
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
editor.canvas.getByText( 'No results found' )
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await pageObject.publishAndGoToFrontend();
|
||||||
|
|
||||||
|
await expect( page.getByText( 'No results found' ) ).toBeVisible();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test.describe( 'Renders correctly with all Product Elements', () => {
|
test.describe( 'Renders correctly with all Product Elements', () => {
|
||||||
const expectedProductContent = [
|
const expectedProductContent = [
|
||||||
'Beanie', // core/post-title
|
'Beanie', // core/post-title
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: enhancement
|
||||||
|
|
||||||
|
Product Collection: Don't render when empty unless the no results block is present.
|
|
@ -47,6 +47,18 @@ class ProductCollection extends AbstractBlock {
|
||||||
protected $custom_order_opts = array( 'popularity', 'rating' );
|
protected $custom_order_opts = array( 'popularity', 'rating' );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The render state of the product collection block.
|
||||||
|
*
|
||||||
|
* These props are runtime-based and reinitialize for every block on a page.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $render_state = array(
|
||||||
|
'has_results' => false,
|
||||||
|
'has_no_results_block' => false,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize this block type.
|
* Initialize this block type.
|
||||||
*
|
*
|
||||||
|
@ -80,8 +92,30 @@ class ProductCollection extends AbstractBlock {
|
||||||
// Provide location context into block's context.
|
// Provide location context into block's context.
|
||||||
add_filter( 'render_block_context', array( $this, 'provide_location_context_for_inner_blocks' ), 11, 1 );
|
add_filter( 'render_block_context', array( $this, 'provide_location_context_for_inner_blocks' ), 11, 1 );
|
||||||
|
|
||||||
|
// Disable block render if the ProductTemplate block is empty.
|
||||||
|
add_filter(
|
||||||
|
'render_block_woocommerce/product-template',
|
||||||
|
function ( $html ) {
|
||||||
|
$this->render_state['has_results'] = ! empty( $html );
|
||||||
|
return $html;
|
||||||
|
},
|
||||||
|
100,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable block render if the ProductCollectionNoResults block is rendered.
|
||||||
|
add_filter(
|
||||||
|
'render_block_woocommerce/product-collection-no-results',
|
||||||
|
function ( $html ) {
|
||||||
|
$this->render_state['has_no_results_block'] = ! empty( $html );
|
||||||
|
return $html;
|
||||||
|
},
|
||||||
|
100,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
|
||||||
// Interactivity API: Add navigation directives to the product collection block.
|
// Interactivity API: Add navigation directives to the product collection block.
|
||||||
add_filter( 'render_block_woocommerce/product-collection', array( $this, 'enhance_product_collection_with_interactivity' ), 10, 2 );
|
add_filter( 'render_block_woocommerce/product-collection', array( $this, 'handle_rendering' ), 10, 2 );
|
||||||
add_filter( 'render_block_core/query-pagination', array( $this, 'add_navigation_link_directives' ), 10, 3 );
|
add_filter( 'render_block_core/query-pagination', array( $this, 'add_navigation_link_directives' ), 10, 3 );
|
||||||
|
|
||||||
add_filter( 'posts_clauses', array( $this, 'add_price_range_filter_posts_clauses' ), 10, 2 );
|
add_filter( 'posts_clauses', array( $this, 'add_price_range_filter_posts_clauses' ), 10, 2 );
|
||||||
|
@ -90,6 +124,46 @@ class ProductCollection extends AbstractBlock {
|
||||||
add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 );
|
add_filter( 'render_block_data', array( $this, 'disable_enhanced_pagination' ), 10, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the rendering of the block.
|
||||||
|
*
|
||||||
|
* @param string $block_content The block content about to be rendered.
|
||||||
|
* @param array $block The block being rendered.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function handle_rendering( $block_content, $block ) {
|
||||||
|
if ( $this->should_prevent_render() ) {
|
||||||
|
return ''; // Prevent rendering.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the render state for the next render.
|
||||||
|
$this->reset_render_state();
|
||||||
|
|
||||||
|
return $this->enhance_product_collection_with_interactivity( $block_content, $block );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the block should be prevented from rendering.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function should_prevent_render() {
|
||||||
|
return ! $this->render_state['has_results'] && ! $this->render_state['has_no_results_block'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the render state.
|
||||||
|
*/
|
||||||
|
private function reset_render_state() {
|
||||||
|
$this->render_state = array(
|
||||||
|
'has_results' => false,
|
||||||
|
'has_no_results_block' => false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the location context to each inner block of the product collection block.
|
* Provides the location context to each inner block of the product collection block.
|
||||||
* Hint: Only blocks using the 'query' context will be affected.
|
* Hint: Only blocks using the 'query' context will be affected.
|
||||||
|
|
Loading…
Reference in New Issue