Skip to content

Commit

Permalink
Editor: Consider single unmodified default block as empty content (#9808
Browse files Browse the repository at this point in the history
)

* Editor: Consider single unmodified default block as empty content

* Editor: Implement content processing inline
  • Loading branch information
aduth authored Sep 17, 2018
1 parent 0816e23 commit dff35fb
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 14 deletions.
61 changes: 54 additions & 7 deletions packages/editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ import createSelector from 'rememo';
/**
* WordPress dependencies
*/
import { serialize, getBlockType, getBlockTypes, hasBlockSupport, hasChildBlocksWithInserterSupport, getUnknownTypeHandlerName } from '@wordpress/blocks';
import {
serialize,
getBlockType,
getBlockTypes,
hasBlockSupport,
hasChildBlocksWithInserterSupport,
getUnknownTypeHandlerName,
isUnmodifiedDefaultBlock,
} from '@wordpress/blocks';
import { moment } from '@wordpress/date';
import { removep } from '@wordpress/autop';

Expand Down Expand Up @@ -349,15 +357,20 @@ export function isEditedPostSaveable( state ) {

/**
* Returns true if the edited post has content. A post has content if it has at
* least one block or otherwise has a non-empty content property assigned.
* least one saveable block or otherwise has a non-empty content property
* assigned.
*
* @param {Object} state Global application state.
*
* @return {boolean} Whether post has content.
*/
export function isEditedPostEmpty( state ) {
// While the condition of truthy content string would be sufficient for
// determining emptiness, testing saveable blocks length is a trivial
// operation by comparison. Since this function can be called frequently,
// optimize for the fast case where saveable blocks are non-empty.
return (
! getBlockCount( state ) &&
! getBlocksForSerialization( state ).length &&
! getEditedPostAttribute( state, 'content' )
);
}
Expand Down Expand Up @@ -1346,6 +1359,31 @@ export function getSuggestedPostFormat( state ) {
return null;
}

/**
* Returns a set of blocks which are to be used in consideration of the post's
* generated save content.
*
* @param {Object} state Editor state.
*
* @return {WPBlock[]} Filtered set of blocks for save.
*/
export function getBlocksForSerialization( state ) {
const blocks = getBlocks( state );

// A single unmodified default block is assumed to be equivalent to an
// empty post.
const isSingleUnmodifiedDefaultBlock = (
blocks.length === 1 &&
isUnmodifiedDefaultBlock( blocks[ 0 ] )
);

if ( isSingleUnmodifiedDefaultBlock ) {
return [];
}

return blocks;
}

/**
* Returns the content of the post being edited, preferring raw string edit
* before falling back to serialization of block state.
Expand All @@ -1361,13 +1399,22 @@ export const getEditedPostContent = createSelector(
return edits.content;
}

const blocks = getBlocks( state );
const blocks = getBlocksForSerialization( state );
const content = serialize( blocks );

// For compatibility purposes, treat a post consisting of a single
// unknown block as legacy content and downgrade to a pre-block-editor
// removep'd content format.
const isSingleUnknownBlock = (
blocks.length === 1 &&
blocks[ 0 ].name === getUnknownTypeHandlerName()
);

if ( blocks.length === 1 && blocks[ 0 ].name === getUnknownTypeHandlerName() ) {
return removep( serialize( blocks ) );
if ( isSingleUnknownBlock ) {
return removep( content );
}

return serialize( blocks );
return content;
},
( state ) => [
state.editor.present.edits.content,
Expand Down
214 changes: 208 additions & 6 deletions packages/editor/src/store/test/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,18 @@ import { filter, without } from 'lodash';
/**
* WordPress dependencies
*/
import { registerBlockType, unregisterBlockType } from '@wordpress/blocks';
import {
registerBlockType,
unregisterBlockType,
createBlock,
getBlockTypes,
getDefaultBlockName,
setDefaultBlockName,
getUnknownTypeHandlerName,
setUnknownTypeHandlerName,
} from '@wordpress/blocks';
import { moment } from '@wordpress/date';
import { RawHTML } from '@wordpress/element';

/**
* Internal dependencies
Expand Down Expand Up @@ -74,6 +84,7 @@ const {
didPostSaveRequestSucceed,
didPostSaveRequestFail,
getSuggestedPostFormat,
getEditedPostContent,
getNotices,
getReusableBlock,
isSavingReusableBlock,
Expand Down Expand Up @@ -101,6 +112,10 @@ describe( 'selectors', () => {
let cachedSelectors;

beforeAll( () => {
cachedSelectors = filter( selectors, ( selector ) => selector.clear );
} );

beforeEach( () => {
registerBlockType( 'core/block', {
save: () => null,
category: 'reusable',
Expand Down Expand Up @@ -138,14 +153,10 @@ describe( 'selectors', () => {
parent: [ 'core/test-block-b' ],
} );

cachedSelectors = filter( selectors, ( selector ) => selector.clear );
} );

beforeEach( () => {
cachedSelectors.forEach( ( { clear } ) => clear() );
} );

afterAll( () => {
afterEach( () => {
unregisterBlockType( 'core/block' );
unregisterBlockType( 'core/test-block-a' );
unregisterBlockType( 'core/test-block-b' );
Expand Down Expand Up @@ -2872,6 +2883,197 @@ describe( 'selectors', () => {
} );
} );

describe( 'getEditedPostContent', () => {
let originalDefaultBlockName, originalUnknownTypeHandlerName;

beforeAll( () => {
originalDefaultBlockName = getDefaultBlockName();
originalUnknownTypeHandlerName = getUnknownTypeHandlerName();

registerBlockType( 'core/default', {
category: 'common',
title: 'default',
attributes: {
modified: {
type: 'boolean',
default: false,
},
},
save: () => null,
} );
registerBlockType( 'core/unknown', {
category: 'common',
title: 'unknown',
attributes: {
html: {
type: 'string',
},
},
save: ( { attributes } ) => <RawHTML>{ attributes.html }</RawHTML>,
} );
setDefaultBlockName( 'core/default' );
setUnknownTypeHandlerName( 'core/unknown' );
} );

afterAll( () => {
setDefaultBlockName( originalDefaultBlockName );
setUnknownTypeHandlerName( originalUnknownTypeHandlerName );
getBlockTypes().forEach( ( block ) => {
unregisterBlockType( block.name );
} );
} );

it( 'defers to returning an edited post attribute', () => {
const block = createBlock( 'core/block' );

const state = {
editor: {
present: {
blockOrder: {
'': [ block.clientId ],
},
blocksByClientId: {
[ block.clientId ]: block,
},
edits: {
content: 'custom edit',
},
},
},
currentPost: {},
};

const content = getEditedPostContent( state );

expect( content ).toBe( 'custom edit' );
} );

it( 'returns serialization of blocks', () => {
const block = createBlock( 'core/block' );

const state = {
editor: {
present: {
blockOrder: {
'': [ block.clientId ],
},
blocksByClientId: {
[ block.clientId ]: block,
},
edits: {},
},
},
currentPost: {},
};

const content = getEditedPostContent( state );

expect( content ).toBe( '<!-- wp:block /-->' );
} );

it( 'returns removep\'d serialization of blocks for single unknown', () => {
const unknownBlock = createBlock( getUnknownTypeHandlerName(), {
html: '<p>foo</p>',
} );
const state = {
editor: {
present: {
blockOrder: {
'': [ unknownBlock.clientId ],
},
blocksByClientId: {
[ unknownBlock.clientId ]: unknownBlock,
},
edits: {},
},
},
currentPost: {},
};

const content = getEditedPostContent( state );

expect( content ).toBe( 'foo' );
} );

it( 'returns non-removep\'d serialization of blocks for multiple unknown', () => {
const firstUnknown = createBlock( getUnknownTypeHandlerName(), {
html: '<p>foo</p>',
} );
const secondUnknown = createBlock( getUnknownTypeHandlerName(), {
html: '<p>bar</p>',
} );
const state = {
editor: {
present: {
blockOrder: {
'': [ firstUnknown.clientId, secondUnknown.clientId ],
},
blocksByClientId: {
[ firstUnknown.clientId ]: firstUnknown,
[ secondUnknown.clientId ]: secondUnknown,
},
edits: {},
},
},
currentPost: {},
};

const content = getEditedPostContent( state );

expect( content ).toBe( '<p>foo</p>\n\n<p>bar</p>' );
} );

it( 'returns empty string for single unmodified default block', () => {
const defaultBlock = createBlock( getDefaultBlockName() );
const state = {
editor: {
present: {
blockOrder: {
'': [ defaultBlock.clientId ],
},
blocksByClientId: {
[ defaultBlock.clientId ]: defaultBlock,
},
edits: {},
},
},
currentPost: {},
};

const content = getEditedPostContent( state );

expect( content ).toBe( '' );
} );

it( 'should not return empty string for modified default block', () => {
const defaultBlock = createBlock( getDefaultBlockName() );
const state = {
editor: {
present: {
blockOrder: {
'': [ defaultBlock.clientId ],
},
blocksByClientId: {
[ defaultBlock.clientId ]: {
...defaultBlock,
attributes: {
...defaultBlock.attributes,
modified: true,
},
},
},
edits: {},
},
},
currentPost: {},
};

const content = getEditedPostContent( state );

expect( content ).toBe( '<!-- wp:default {\"modified\":true} /-->' );
} );
} );

describe( 'getNotices', () => {
it( 'should return the notices array', () => {
const state = {
Expand Down
6 changes: 5 additions & 1 deletion test/e2e/specs/__snapshots__/splitting-merging.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

exports[`splitting and merging blocks should delete an empty first line 1`] = `
"<!-- wp:paragraph -->
<p></p>
<p>First</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Still Second</p>
<!-- /wp:paragraph -->"
`;

Expand Down
10 changes: 10 additions & 0 deletions test/e2e/specs/splitting-merging.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,24 @@ describe( 'splitting and merging blocks', () => {
// should remove the first.
//
// See: https://github.com/WordPress/gutenberg/issues/8388

// First paragraph
await insertBlock( 'Paragraph' );
await page.keyboard.type( 'First' );
await page.keyboard.press( 'Enter' );

// Second paragraph
await page.keyboard.down( 'Shift' );
await page.keyboard.press( 'Enter' );
await page.keyboard.up( 'Shift' );

// Delete the soft line break.
await page.keyboard.press( 'Backspace' );

// Typing at this point should occur still within the second paragraph,
// while before the regression fix it would have occurred in the first.
await page.keyboard.type( 'Still Second' );

expect( await getEditedPostContent() ).toMatchSnapshot();
} );

Expand Down

0 comments on commit dff35fb

Please sign in to comment.