diff --git a/blocks/api/parser.js b/blocks/api/parser.js index 644fa7cc12072..2aa0da801fd85 100644 --- a/blocks/api/parser.js +++ b/blocks/api/parser.js @@ -11,6 +11,7 @@ import { parse as grammarParse } from './post.pegjs'; import { getBlockType, getUnknownTypeHandlerName } from './registration'; import { createBlock } from './factory'; import { isValidBlock } from './validation'; +import { getCommentDelimitedContent } from './serializer'; /** * Returns true if the provided function is a valid attribute source, or false @@ -177,6 +178,12 @@ export function createBlockWithFallback( name, rawContent, attributes ) { let blockType = getBlockType( name ); const fallbackBlock = getUnknownTypeHandlerName(); if ( ! blockType ) { + // If detected as a block which is not registered, preserve comment + // delimiters in content of unknown type handler. + if ( name ) { + rawContent = getCommentDelimitedContent( name, attributes, rawContent ); + } + name = fallbackBlock; blockType = getBlockType( name ); } diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index feb6ceae9c6d4..e9322096dc829 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -13,7 +13,7 @@ import { Component, createElement, renderToString, cloneElement, Children } from /** * Internal dependencies */ -import { getBlockType } from './registration'; +import { getBlockType, getUnknownTypeHandlerName } from './registration'; /** * Returns the block's default classname from its name @@ -139,6 +139,37 @@ export function getBeautifulContent( content ) { } ); } +/** + * Returns the content of a block, including comment delimiters. + * + * @param {String} blockName Block name + * @param {Object} attributes Block attributes + * @param {String} content Block save content + * @return {String} Comment-delimited block content + */ +export function getCommentDelimitedContent( blockName, attributes, content ) { + const serializedAttributes = ! isEmpty( attributes ) + ? serializeAttributes( attributes ) + ' ' + : ''; + + if ( ! content ) { + return ``; + } + + return ( + `\n` + + getBeautifulContent( content ) + + `\n` + ); +} + +/** + * Returns the content of a block, including comment delimiters, determining + * serialized attributes and content form from the current state of the block. + * + * @param {Object} block Block instance + * @return {String} Serialized block + */ export function serializeBlock( block ) { const blockName = block.name; const blockType = getBlockType( blockName ); @@ -154,23 +185,17 @@ export function serializeBlock( block ) { const saveAttributes = getCommentAttributes( block.attributes, blockType.attributes ); - if ( 'core/more' === blockName ) { - return `${ saveAttributes.noTeaser ? '\n' : '' }`; - } + switch ( blockName ) { + case 'core/more': + const { text, noTeaser } = saveAttributes; + return `${ noTeaser ? '\n' : '' }`; - const serializedAttributes = ! isEmpty( saveAttributes ) - ? serializeAttributes( saveAttributes ) + ' ' - : ''; + case getUnknownTypeHandlerName(): + return saveContent; - if ( ! saveContent ) { - return ``; + default: + return getCommentDelimitedContent( blockName, saveAttributes, saveContent ); } - - return ( - `\n` + - getBeautifulContent( saveContent ) + - `\n` - ); } /** diff --git a/blocks/api/test/parser.js b/blocks/api/test/parser.js index 49fb5a82b18ba..747b8e8ef1dce 100644 --- a/blocks/api/test/parser.js +++ b/blocks/api/test/parser.js @@ -199,7 +199,19 @@ describe( 'block parser', () => { } ); it( 'should fall back to the unknown type handler for unknown blocks if present', () => { - registerBlockType( 'core/unknown-block', defaultBlockSettings ); + registerBlockType( 'core/unknown-block', { + category: 'common', + attributes: { + content: { + type: 'string', + source: html(), + }, + fruit: { + type: 'string', + }, + }, + save: ( { attributes } ) => attributes.content, + } ); setUnknownTypeHandlerName( 'core/unknown-block' ); const block = createBlockWithFallback( @@ -207,8 +219,9 @@ describe( 'block parser', () => { 'content', { fruit: 'Bananas' } ); - expect( block.name ).toEqual( 'core/unknown-block' ); - expect( block.attributes ).toEqual( { fruit: 'Bananas' } ); + expect( block.name ).toBe( 'core/unknown-block' ); + expect( block.attributes.fruit ).toBe( 'Bananas' ); + expect( block.attributes.content ).toContain( 'core/test-block' ); } ); it( 'should fall back to the unknown type handler if block type not specified', () => { diff --git a/blocks/api/test/serializer.js b/blocks/api/test/serializer.js index 9ddba1e639bb8..b6c8b27ad38e4 100644 --- a/blocks/api/test/serializer.js +++ b/blocks/api/test/serializer.js @@ -12,12 +12,20 @@ import serialize, { getBeautifulContent, getSaveContent, serializeAttributes, + getCommentDelimitedContent, + serializeBlock, } from '../serializer'; -import { getBlockTypes, registerBlockType, unregisterBlockType } from '../registration'; +import { + getBlockTypes, + registerBlockType, + unregisterBlockType, + setUnknownTypeHandlerName, +} from '../registration'; import { createBlock } from '../'; describe( 'block serializer', () => { afterEach( () => { + setUnknownTypeHandlerName( undefined ); getBlockTypes().forEach( block => { unregisterBlockType( block.name ); } ); @@ -210,6 +218,120 @@ describe( 'block serializer', () => { } ); } ); + describe( 'getCommentDelimitedContent()', () => { + it( 'should generate empty attributes void', () => { + const content = getCommentDelimitedContent( + 'core/test-block', + {}, + '' + ); + + expect( content ).toBe( '' ); + } ); + + it( 'should generate empty attributes non-void', () => { + const content = getCommentDelimitedContent( + 'core/test-block', + {}, + 'Delicious' + ); + + expect( content ).toBe( '\nDelicious\n' ); + } ); + + it( 'should generate non-empty attributes void', () => { + const content = getCommentDelimitedContent( + 'core/test-block', + { fruit: 'Banana' }, + '' + ); + + expect( content ).toBe( + '' + ); + } ); + + it( 'should generate non-empty attributes non-void', () => { + const content = getCommentDelimitedContent( + 'core/test-block', + { fruit: 'Banana' }, + 'Delicious' + ); + + expect( content ).toBe( + '\nDelicious\n' + ); + } ); + } ); + + describe( 'serializeBlock()', () => { + describe( '"more" block', () => { + beforeEach( () => { + registerBlockType( 'core/more', { + category: 'layout', + + attributes: { + text: { + type: 'string', + }, + noTeaser: { + type: 'boolean', + default: false, + }, + }, + + save: ( { attributes } ) => attributes.text, + } ); + } ); + + it( 'serializes without text', () => { + const block = createBlock( 'core/more', {} ); + + const content = serializeBlock( block ); + + expect( content ).toBe( '' ); + } ); + + it( 'serializes with text', () => { + const block = createBlock( 'core/more', { + text: 'Read more!', + } ); + + const content = serializeBlock( block ); + + expect( content ).toBe( '' ); + } ); + + it( 'serializes with no teaser', () => { + const block = createBlock( 'core/more', { + noTeaser: true, + } ); + + const content = serializeBlock( block ); + + expect( content ).toBe( '\n' ); + } ); + } ); + + it( 'serializes the fallback block without comment delimiters', () => { + registerBlockType( 'core/unknown-block', { + category: 'common', + attributes: { + fruit: { + type: 'string', + }, + }, + save: ( { attributes } ) => attributes.fruit, + } ); + setUnknownTypeHandlerName( 'core/unknown-block' ); + const block = createBlock( 'core/unknown-block', { fruit: 'Bananas' } ); + + const content = serializeBlock( block ); + + expect( content ).toBe( 'Bananas' ); + } ); + } ); + describe( 'serialize()', () => { it( 'should serialize the post content properly', () => { const blockType = { diff --git a/blocks/test/fixtures/core__audio.parsed.json b/blocks/test/fixtures/core__audio.parsed.json index 1eb91c18f73e3..1066d69b9570b 100644 --- a/blocks/test/fixtures/core__audio.parsed.json +++ b/blocks/test/fixtures/core__audio.parsed.json @@ -2,7 +2,7 @@ { "blockName": "core/audio", "attrs": { - "align": "right" + "align": "right" }, "rawContent": "\n
\n \n
\n" }, diff --git a/blocks/test/fixtures/core__freeform.serialized.html b/blocks/test/fixtures/core__freeform.serialized.html index 0bf5ad6d0d829..2c349303c54e0 100644 --- a/blocks/test/fixtures/core__freeform.serialized.html +++ b/blocks/test/fixtures/core__freeform.serialized.html @@ -1,6 +1,4 @@ - Testing freeform block with some
- HTML content + HTML content
-