diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/pattern-blocks.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/pattern-blocks.test.js.snap index ffa6af8c2b733..325deae67bbfa 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/pattern-blocks.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/pattern-blocks.test.js.snap @@ -1,11 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Pattern blocks allows conversion back to blocks when the reusable block has unsaved edits 1`] = ` -" -

1

-" -`; - exports[`Pattern blocks can be created from multiselection and converted back to regular blocks 1`] = ` "

Hello there!

diff --git a/packages/e2e-tests/specs/editor/various/pattern-blocks.test.js b/packages/e2e-tests/specs/editor/various/pattern-blocks.test.js index b8021143259a8..32d5952f57259 100644 --- a/packages/e2e-tests/specs/editor/various/pattern-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/pattern-blocks.test.js @@ -252,34 +252,6 @@ describe( 'Pattern blocks', () => { ); } ); - // Check for regressions of https://github.com/WordPress/gutenberg/issues/26421. - // Skip reason: This is broken at the time with Pattern Overrides. - // See https://github.com/WordPress/gutenberg/issues/58122 - it.skip( 'allows conversion back to blocks when the reusable block has unsaved edits', async () => { - await createReusableBlock( '1', 'Edited block' ); - - // Make an edit to the reusable block and assert that there's only a - // paragraph in a reusable block. - await canvas().waitForSelector( 'p[aria-label="Block: Paragraph"]' ); - await canvas().click( 'p[aria-label="Block: Paragraph"]' ); - await page.keyboard.type( '2' ); - const selector = - '//div[@aria-label="Block: Pattern"]//p[@aria-label="Block: Paragraph"][.="12"]'; - const reusableBlockWithParagraph = await page.$x( selector ); - expect( reusableBlockWithParagraph ).toBeTruthy(); - - // Convert back to regular blocks. - await clickBlockToolbarButton( 'Select parent block: Edited block' ); - await clickBlockToolbarButton( 'Options' ); - await clickMenuItem( 'Detach' ); - await page.waitForXPath( selector, { - hidden: true, - } ); - - // Check that there's only a paragraph. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - // Test for regressions of https://github.com/WordPress/gutenberg/issues/27243. it( 'should allow a block with styles to be converted to a reusable block', async () => { // Insert a quote and reload the page. diff --git a/packages/patterns/src/store/actions.js b/packages/patterns/src/store/actions.js index dfa0326a70941..7b9090ae4de66 100644 --- a/packages/patterns/src/store/actions.js +++ b/packages/patterns/src/store/actions.js @@ -2,7 +2,7 @@ * WordPress dependencies */ -import { parse } from '@wordpress/blocks'; +import { cloneBlock } from '@wordpress/blocks'; import { store as coreStore } from '@wordpress/core-data'; import { store as blockEditorStore } from '@wordpress/block-editor'; @@ -90,25 +90,37 @@ export const createPatternFromFile = export const convertSyncedPatternToStatic = ( clientId ) => ( { registry } ) => { - const oldBlock = registry + const patternBlock = registry .select( blockEditorStore ) .getBlock( clientId ); - const pattern = registry - .select( 'core' ) - .getEditedEntityRecord( - 'postType', - 'wp_block', - oldBlock.attributes.ref - ); - const newBlocks = parse( - typeof pattern.content === 'function' - ? pattern.content( pattern ) - : pattern.content - ); + function cloneBlocksAndRemoveBindings( blocks ) { + return blocks.map( ( block ) => { + let metadata = block.attributes.metadata; + if ( metadata ) { + metadata = { ...metadata }; + delete metadata.id; + delete metadata.bindings; + } + return cloneBlock( + block, + { + metadata: + metadata && Object.keys( metadata ).length > 0 + ? metadata + : undefined, + }, + cloneBlocksAndRemoveBindings( block.innerBlocks ) + ); + } ); + } + registry .dispatch( blockEditorStore ) - .replaceBlocks( oldBlock.clientId, newBlocks ); + .replaceBlocks( + patternBlock.clientId, + cloneBlocksAndRemoveBindings( patternBlock.innerBlocks ) + ); }; /** diff --git a/test/e2e/specs/editor/various/pattern-overrides.spec.js b/test/e2e/specs/editor/various/pattern-overrides.spec.js index 62a993c5d9cf2..4e7bc80fdff5d 100644 --- a/test/e2e/specs/editor/various/pattern-overrides.spec.js +++ b/test/e2e/specs/editor/various/pattern-overrides.spec.js @@ -216,4 +216,51 @@ test.describe( 'Pattern Overrides', () => { ] ); } ); } ); + + test( 'retains override values when converting a pattern block to regular blocks', async ( { + page, + admin, + requestUtils, + editor, + } ) => { + const paragraphId = 'paragraph-id'; + const { id } = await requestUtils.createBlock( { + title: 'Pattern', + content: ` +

Editable

+`, + status: 'publish', + } ); + + await admin.createNewPost(); + + await editor.insertBlock( { + name: 'core/block', + attributes: { ref: id }, + } ); + + // Make an edit to the pattern. + await editor.canvas + .getByRole( 'document', { name: 'Block: Paragraph' } ) + .focus(); + await page.keyboard.type( 'edited ' ); + + // Convert back to regular blocks. + await editor.selectBlocks( + editor.canvas.getByRole( 'document', { name: 'Block: Pattern' } ) + ); + await editor.showBlockToolbar(); + await editor.clickBlockOptionsMenuItem( 'Detach' ); + + // Check that the overrides remain. + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: 'edited Editable', + metadata: undefined, + }, + }, + ] ); + } ); } ); diff --git a/test/e2e/specs/editor/various/patterns.spec.js b/test/e2e/specs/editor/various/patterns.spec.js index c53c6fef31928..745f5a5aa417b 100644 --- a/test/e2e/specs/editor/various/patterns.spec.js +++ b/test/e2e/specs/editor/various/patterns.spec.js @@ -252,4 +252,73 @@ test.describe( 'Synced pattern', () => { }, ] ); } ); + + // Check for regressions of https://github.com/WordPress/gutenberg/issues/26421. + test( 'allows conversion back to blocks when the reusable block has unsaved edits', async ( { + page, + requestUtils, + editor, + } ) => { + const { id } = await requestUtils.createBlock( { + title: 'Synced pattern', + content: + '\n

Before Edit

\n', + status: 'publish', + } ); + + await editor.insertBlock( { + name: 'core/block', + attributes: { ref: id }, + } ); + + await editor.selectBlocks( + editor.canvas.getByRole( 'document', { name: 'Block: Pattern' } ) + ); + await page + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'link', { name: 'Edit original' } ) + .click(); + + const editorTopBar = page.getByRole( 'region', { + name: 'Editor top bar', + } ); + + // Navigate to the pattern focus mode. + await expect( + editorTopBar.getByRole( 'heading', { + name: 'Synced pattern', + level: 1, + } ) + ).toBeVisible(); + + // Make an edit to the source pattern. + await editor.canvas + .getByRole( 'document', { name: 'Block: Paragraph' } ) + .fill( 'After Edit' ); + + // Go back to the post. + await editorTopBar.getByRole( 'button', { name: 'Back' } ).click(); + + const expectedParagraphBlock = { + name: 'core/paragraph', + attributes: { content: 'After Edit' }, + }; + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/block', + attributes: { ref: id }, + innerBlocks: [ expectedParagraphBlock ], + }, + ] ); + + await editor.selectBlocks( + editor.canvas.getByRole( 'document', { name: 'Block: Pattern' } ) + ); + await editor.clickBlockOptionsMenuItem( 'Detach' ); + + await expect + .poll( editor.getBlocks ) + .toMatchObject( [ expectedParagraphBlock ] ); + } ); } );