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();
|
||||
} );
|
||||
|
||||
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', () => {
|
||||
const expectedProductContent = [
|
||||
'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' );
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -80,8 +92,30 @@ class ProductCollection extends AbstractBlock {
|
|||
// Provide location context into block's context.
|
||||
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.
|
||||
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( '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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Hint: Only blocks using the 'query' context will be affected.
|
||||
|
|
Loading…
Reference in New Issue