Fix Classic Template block registration on WP 6.6 (#48730)
* Fix Classic Template block registration on WP 6.6 * Update Compatibility Layer docs * Add changelog file * Remove unused function * Simplify logic
This commit is contained in:
parent
d9e837a8ca
commit
0205513556
|
@ -7,7 +7,6 @@ import {
|
|||
getBlockType,
|
||||
registerBlockType,
|
||||
unregisterBlockType,
|
||||
parse,
|
||||
} from '@wordpress/blocks';
|
||||
import type { BlockEditProps } from '@wordpress/blocks';
|
||||
import { WC_BLOCKS_IMAGE_URL } from '@woocommerce/block-settings';
|
||||
|
@ -19,19 +18,11 @@ import {
|
|||
import { Button, Placeholder, Popover } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { box, Icon } from '@wordpress/icons';
|
||||
import {
|
||||
useDispatch,
|
||||
subscribe,
|
||||
useSelect,
|
||||
select,
|
||||
dispatch,
|
||||
} from '@wordpress/data';
|
||||
import { useDispatch, subscribe, useSelect, select } from '@wordpress/data';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import { store as noticesStore } from '@wordpress/notices';
|
||||
import { useEntityRecord } from '@wordpress/core-data';
|
||||
import { debounce } from '@woocommerce/base-utils';
|
||||
import { woo } from '@woocommerce/icons';
|
||||
import { isNumber } from '@woocommerce/types';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -296,7 +287,7 @@ const registerClassicTemplateBlock = ( {
|
|||
template,
|
||||
inserter,
|
||||
}: {
|
||||
template?: string;
|
||||
template?: string | null;
|
||||
inserter: boolean;
|
||||
} ) => {
|
||||
/**
|
||||
|
@ -370,101 +361,83 @@ const registerClassicTemplateBlock = ( {
|
|||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to recover the Classic Template block if it fails to render on the Single Product template
|
||||
* due to the user resetting customizations without refreshing the page.
|
||||
*
|
||||
* When the Classic Template block fails to render, it is replaced by the 'core/missing' block, which
|
||||
* displays an error message stating that the WooCommerce Classic template block is unsupported.
|
||||
*
|
||||
* This function replaces the 'core/missing' block with the original Classic Template block that failed
|
||||
* to render, allowing the block to be displayed correctly.
|
||||
*
|
||||
* @see {@link https://github.com/woocommerce/woocommerce-blocks/issues/9637|Issue: Block error is displayed on clearing customizations for Woo Templates}
|
||||
*
|
||||
*/
|
||||
const tryToRecoverClassicTemplateBlockWhenItFailsToRender = debounce( () => {
|
||||
const blocks = select( 'core/block-editor' ).getBlocks();
|
||||
const blocksIncludingInnerBlocks = blocks.flatMap( ( block ) => [
|
||||
block,
|
||||
...block.innerBlocks,
|
||||
] );
|
||||
const classicTemplateThatFailedToRender = blocksIncludingInnerBlocks.find(
|
||||
( block ) =>
|
||||
block.name === 'core/missing' &&
|
||||
block.attributes.originalName === BLOCK_SLUG
|
||||
);
|
||||
|
||||
if ( classicTemplateThatFailedToRender ) {
|
||||
const blockToReplaceClassicTemplateBlockThatFailedToRender = parse(
|
||||
classicTemplateThatFailedToRender.attributes.originalContent
|
||||
);
|
||||
if ( blockToReplaceClassicTemplateBlockThatFailedToRender ) {
|
||||
dispatch( 'core/block-editor' ).replaceBlock(
|
||||
classicTemplateThatFailedToRender.clientId,
|
||||
blockToReplaceClassicTemplateBlockThatFailedToRender
|
||||
);
|
||||
}
|
||||
}
|
||||
}, 100 );
|
||||
|
||||
// @todo Refactor when there will be possible to show a block according on a template/post with a Gutenberg API. https://github.com/WordPress/gutenberg/pull/41718
|
||||
|
||||
let currentTemplateId: string | undefined;
|
||||
let previousEditedTemplate: string | number | null = null;
|
||||
let isBlockRegistered = false;
|
||||
let isBlockInInserter = false;
|
||||
const handleRegisterClassicTemplateBlock = ( {
|
||||
template,
|
||||
inserter,
|
||||
}: {
|
||||
template: string | null;
|
||||
inserter: boolean;
|
||||
} ) => {
|
||||
if ( isBlockRegistered ) {
|
||||
unregisterBlockType( BLOCK_SLUG );
|
||||
}
|
||||
isBlockInInserter = inserter;
|
||||
isBlockRegistered = true;
|
||||
registerClassicTemplateBlock( {
|
||||
template,
|
||||
inserter,
|
||||
} );
|
||||
};
|
||||
|
||||
subscribe( () => {
|
||||
const previousTemplateId = currentTemplateId;
|
||||
const store = select( 'core/edit-site' );
|
||||
// With GB 16.3.0 the return type can be a number: https://github.com/WordPress/gutenberg/issues/53230
|
||||
const editedPostId = store?.getEditedPostId() as
|
||||
| string
|
||||
| number
|
||||
| undefined;
|
||||
const editorStore = select( 'core/editor' );
|
||||
// We use blockCount to know if we are editing a template or in the navigation.
|
||||
const blockCount = editorStore?.getBlockCount() as number;
|
||||
const templateSlug = editorStore?.getEditedPostSlug() as string | null;
|
||||
const editedTemplate = blockCount && blockCount > 0 ? templateSlug : null;
|
||||
|
||||
currentTemplateId = isNumber( editedPostId ) ? undefined : editedPostId;
|
||||
// Skip if we are in the same template, except if the block hasn't been registered yet.
|
||||
if ( isBlockRegistered && previousEditedTemplate === editedTemplate ) {
|
||||
return;
|
||||
}
|
||||
previousEditedTemplate = editedTemplate;
|
||||
|
||||
const parsedTemplate = currentTemplateId?.split( '//' )[ 1 ];
|
||||
|
||||
if ( parsedTemplate === null || parsedTemplate === undefined ) {
|
||||
// Handle the case when we are not editing a template (ie: in the navigation screen).
|
||||
if ( ! editedTemplate ) {
|
||||
if ( ! isBlockRegistered ) {
|
||||
handleRegisterClassicTemplateBlock( {
|
||||
template: editedTemplate,
|
||||
inserter: false,
|
||||
} );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const block = getBlockType( BLOCK_SLUG );
|
||||
const isBlockRegistered = Boolean( block );
|
||||
const templateSupportsClassicTemplateBlock =
|
||||
hasTemplateSupportForClassicTemplateBlock( editedTemplate, TEMPLATES );
|
||||
|
||||
if (
|
||||
isBlockRegistered &&
|
||||
hasTemplateSupportForClassicTemplateBlock( parsedTemplate, TEMPLATES )
|
||||
) {
|
||||
tryToRecoverClassicTemplateBlockWhenItFailsToRender();
|
||||
}
|
||||
|
||||
if ( previousTemplateId === currentTemplateId ) {
|
||||
// Handle the case when we are editing a template that doesn't support the Classic Template block (ie: Blog Home).
|
||||
if ( ! templateSupportsClassicTemplateBlock && isBlockInInserter ) {
|
||||
handleRegisterClassicTemplateBlock( {
|
||||
template: editedTemplate,
|
||||
inserter: false,
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isBlockRegistered &&
|
||||
( ! hasTemplateSupportForClassicTemplateBlock(
|
||||
parsedTemplate,
|
||||
TEMPLATES
|
||||
) ||
|
||||
isClassicTemplateBlockRegisteredWithAnotherTitle(
|
||||
block,
|
||||
parsedTemplate
|
||||
) )
|
||||
) {
|
||||
unregisterBlockType( BLOCK_SLUG );
|
||||
currentTemplateId = undefined;
|
||||
// Handle the case when we are editing a template that does support the Classic Template block (ie: Product Catalog).
|
||||
if ( templateSupportsClassicTemplateBlock && ! isBlockInInserter ) {
|
||||
handleRegisterClassicTemplateBlock( {
|
||||
template: editedTemplate,
|
||||
inserter: true,
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the case when we are editing a template that does support the Classic Template block but it's currently registered with another title (ie: navigating from the Product Catalog template to the Product Search Results template).
|
||||
if (
|
||||
! isBlockRegistered &&
|
||||
hasTemplateSupportForClassicTemplateBlock( parsedTemplate, TEMPLATES )
|
||||
templateSupportsClassicTemplateBlock &&
|
||||
isClassicTemplateBlockRegisteredWithAnotherTitle(
|
||||
getBlockType( BLOCK_SLUG ),
|
||||
editedTemplate
|
||||
)
|
||||
) {
|
||||
registerClassicTemplateBlock( {
|
||||
template: parsedTemplate,
|
||||
handleRegisterClassicTemplateBlock( {
|
||||
template: editedTemplate,
|
||||
inserter: true,
|
||||
} );
|
||||
}
|
||||
|
|
|
@ -6,8 +6,9 @@ The Compatibility Layer is disabled when either of classic template blocks are a
|
|||
|
||||
- `Product (Classic)`,
|
||||
- `Product Attribute (Classic)`,
|
||||
- `Product Taxonomy (Classic)`,
|
||||
- `Product Category (Classic)`,
|
||||
- `Product Tag (Classic)`,
|
||||
- `Product's Custom Taxonomy (Classic)`,
|
||||
- `Product Search Results (Classic)`,
|
||||
- `Product Grid (Classic)`.
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { test, expect, wpCLI, BlockData } from '@woocommerce/e2e-utils';
|
||||
import { test, expect, wpCLI, BlockData, Editor } from '@woocommerce/e2e-utils';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -11,6 +11,17 @@ const blockData: Partial< BlockData > = {
|
|||
name: 'woocommerce/legacy-template',
|
||||
};
|
||||
|
||||
const classicTemplateBlockNames = [
|
||||
'WooCommerce Classic Template',
|
||||
'Product (Classic)',
|
||||
'Product Attribute (Classic)',
|
||||
'Product Category (Classic)',
|
||||
'Product Tag (Classic)',
|
||||
"Product's Custom Taxonomy (Classic)",
|
||||
'Product Search Results (Classic)',
|
||||
'Product Grid (Classic)',
|
||||
];
|
||||
|
||||
const templates = [
|
||||
{
|
||||
title: 'Single Product',
|
||||
|
@ -46,6 +57,47 @@ const templates = [
|
|||
},
|
||||
];
|
||||
|
||||
const getClassicTemplateBlocksInInserter = async ( {
|
||||
editor,
|
||||
}: {
|
||||
editor: Editor;
|
||||
} ) => {
|
||||
await editor.openGlobalBlockInserter();
|
||||
|
||||
await editor.page
|
||||
.getByLabel( 'Search for blocks and patterns' )
|
||||
.fill( 'classic' );
|
||||
|
||||
// Wait for blocks search to have finished.
|
||||
await expect(
|
||||
editor.page.getByRole( 'heading', {
|
||||
name: 'Available to install',
|
||||
exact: true,
|
||||
} )
|
||||
).toBeVisible();
|
||||
|
||||
const inserterBlocks = editor.page.getByRole( 'listbox', {
|
||||
name: 'Blocks',
|
||||
exact: true,
|
||||
} );
|
||||
const options = inserterBlocks.locator( 'role=option' );
|
||||
|
||||
// Filter out blocks that don't match one of the possible Classic Template block names (case-insensitive).
|
||||
const classicTemplateBlocks = await options.evaluateAll(
|
||||
( elements, blockNames ) => {
|
||||
const blockOptions = elements.filter( ( element ) => {
|
||||
return blockNames.some(
|
||||
( name ) => element.textContent === name
|
||||
);
|
||||
} );
|
||||
return blockOptions.map( ( element ) => element.textContent );
|
||||
},
|
||||
classicTemplateBlockNames
|
||||
);
|
||||
|
||||
return classicTemplateBlocks;
|
||||
};
|
||||
|
||||
test.describe( `${ blockData.name } Block `, () => {
|
||||
test.beforeEach( async () => {
|
||||
await wpCLI(
|
||||
|
@ -53,6 +105,176 @@ test.describe( `${ blockData.name } Block `, () => {
|
|||
);
|
||||
} );
|
||||
|
||||
test( `is registered/unregistered when navigating from a non-WC template to a WC template and back`, async ( {
|
||||
admin,
|
||||
editor,
|
||||
} ) => {
|
||||
await admin.visitSiteEditor( {
|
||||
postId: `twentytwentyfour//home`,
|
||||
postType: 'wp_template',
|
||||
canvas: 'edit',
|
||||
} );
|
||||
|
||||
let classicTemplateBlocks = await getClassicTemplateBlocksInInserter( {
|
||||
editor,
|
||||
} );
|
||||
|
||||
expect( classicTemplateBlocks ).toHaveLength( 0 );
|
||||
|
||||
await editor.page.getByLabel( 'Open Navigation' ).click();
|
||||
await editor.page
|
||||
.getByLabel( 'Product Catalog', { exact: true } )
|
||||
.click();
|
||||
|
||||
classicTemplateBlocks = await getClassicTemplateBlocksInInserter( {
|
||||
editor,
|
||||
} );
|
||||
|
||||
expect( classicTemplateBlocks ).toHaveLength( 1 );
|
||||
|
||||
await editor.page.getByLabel( 'Open Navigation' ).click();
|
||||
await editor.page.getByLabel( 'Blog Home', { exact: true } ).click();
|
||||
|
||||
classicTemplateBlocks = await getClassicTemplateBlocksInInserter( {
|
||||
editor,
|
||||
} );
|
||||
|
||||
expect( classicTemplateBlocks ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
test( `is registered/unregistered when navigating from a WC template to a non-WC template and back`, async ( {
|
||||
admin,
|
||||
editor,
|
||||
} ) => {
|
||||
await admin.visitSiteEditor( {
|
||||
postId: `woocommerce/woocommerce//archive-product`,
|
||||
postType: 'wp_template',
|
||||
canvas: 'edit',
|
||||
} );
|
||||
|
||||
let classicTemplateBlocks = await getClassicTemplateBlocksInInserter( {
|
||||
editor,
|
||||
} );
|
||||
|
||||
expect( classicTemplateBlocks ).toHaveLength( 1 );
|
||||
|
||||
await editor.page.getByLabel( 'Open Navigation' ).click();
|
||||
await editor.page.getByLabel( 'Blog Home', { exact: true } ).click();
|
||||
|
||||
classicTemplateBlocks = await getClassicTemplateBlocksInInserter( {
|
||||
editor,
|
||||
} );
|
||||
|
||||
expect( classicTemplateBlocks ).toHaveLength( 0 );
|
||||
|
||||
await editor.page.getByLabel( 'Open Navigation' ).click();
|
||||
await editor.page
|
||||
.getByLabel( 'Product Catalog', { exact: true } )
|
||||
.click();
|
||||
|
||||
classicTemplateBlocks = await getClassicTemplateBlocksInInserter( {
|
||||
editor,
|
||||
} );
|
||||
|
||||
expect( classicTemplateBlocks ).toHaveLength( 1 );
|
||||
} );
|
||||
|
||||
test( `updates block title when navigating between WC templates`, async ( {
|
||||
admin,
|
||||
editor,
|
||||
} ) => {
|
||||
await admin.visitSiteEditor( {
|
||||
postId: `woocommerce/woocommerce//archive-product`,
|
||||
postType: 'wp_template',
|
||||
canvas: 'edit',
|
||||
} );
|
||||
|
||||
let classicTemplateBlocks = await getClassicTemplateBlocksInInserter( {
|
||||
editor,
|
||||
} );
|
||||
|
||||
expect( classicTemplateBlocks[ 0 ] ).toBe( 'Product Grid (Classic)' );
|
||||
|
||||
await editor.page.getByLabel( 'Open Navigation' ).click();
|
||||
await editor.page
|
||||
.getByLabel( 'Product Search Results', { exact: true } )
|
||||
.click();
|
||||
|
||||
classicTemplateBlocks = await getClassicTemplateBlocksInInserter( {
|
||||
editor,
|
||||
} );
|
||||
|
||||
expect( classicTemplateBlocks[ 0 ] ).toBe(
|
||||
'Product Search Results (Classic)'
|
||||
);
|
||||
} );
|
||||
|
||||
test( `is not available when editing template parts`, async ( {
|
||||
admin,
|
||||
editor,
|
||||
} ) => {
|
||||
await admin.visitSiteEditor( {
|
||||
postId: `twentytwentyfour//header`,
|
||||
postType: 'wp_template_part',
|
||||
canvas: 'edit',
|
||||
} );
|
||||
|
||||
const classicTemplateBlocks = await getClassicTemplateBlocksInInserter(
|
||||
{
|
||||
editor,
|
||||
}
|
||||
);
|
||||
|
||||
expect( classicTemplateBlocks ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
// @see https://github.com/woocommerce/woocommerce-blocks/issues/9637
|
||||
test( `is still available after resetting a modified WC template`, async ( {
|
||||
admin,
|
||||
editor,
|
||||
} ) => {
|
||||
await admin.visitSiteEditor( {
|
||||
postId: `woocommerce/woocommerce//single-product`,
|
||||
postType: 'wp_template',
|
||||
canvas: 'edit',
|
||||
} );
|
||||
|
||||
await editor.insertBlock( {
|
||||
name: 'core/paragraph',
|
||||
attributes: { content: 'Hello World' },
|
||||
} );
|
||||
|
||||
await editor.saveSiteEditorEntities( {
|
||||
isOnlyCurrentEntityDirty: true,
|
||||
} );
|
||||
|
||||
await editor.page.getByLabel( 'Open Navigation' ).click();
|
||||
|
||||
// Reset the template.
|
||||
await editor.page.getByPlaceholder( 'Search' ).fill( 'Single Product' );
|
||||
const resetNotice = editor.page
|
||||
.getByLabel( 'Dismiss this notice' )
|
||||
.getByText( `"Single Product" reset.` );
|
||||
const searchResults = editor.page.getByLabel( 'Actions' );
|
||||
await expect.poll( async () => await searchResults.count() ).toBe( 1 );
|
||||
await searchResults.first().click();
|
||||
await editor.page.getByRole( 'menuitem', { name: 'Reset' } ).click();
|
||||
await editor.page.getByRole( 'button', { name: 'Reset' } ).click();
|
||||
await expect( resetNotice ).toBeVisible();
|
||||
|
||||
// Open the template again.
|
||||
await editor.page.getByRole( 'menuitem', { name: 'Edit' } ).click();
|
||||
|
||||
// Verify the Classic Template block is still registered.
|
||||
const classicTemplateBlocks = await getClassicTemplateBlocksInInserter(
|
||||
{
|
||||
editor,
|
||||
}
|
||||
);
|
||||
|
||||
expect( classicTemplateBlocks ).toHaveLength( 1 );
|
||||
} );
|
||||
|
||||
for ( const template of templates ) {
|
||||
test( `is rendered on ${ template.title } template`, async ( {
|
||||
admin,
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: update
|
||||
|
||||
Fix Classic Template block registration on WP 6.6
|
Loading…
Reference in New Issue