Skip to content

Commit

Permalink
Dont trigger invalid block on empty HTML content (WordPress#9546)
Browse files Browse the repository at this point in the history
* Dont trigger invalid block on empty HTML content

If a block’s HTML is set to the empty string we reset the block to it’s default HTML and mark it as valid. This is more useful than the block becoming invalid and needing additional work to resolve

* Only update HTML content if block is reset
  • Loading branch information
johngodley authored Sep 13, 2018
1 parent 5ea0283 commit 4c579ed
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 5 deletions.
20 changes: 15 additions & 5 deletions packages/editor/src/components/block-list/block-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { isEqual } from 'lodash';
*/
import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import { getBlockAttributes, getBlockContent, getBlockType, isValidBlock } from '@wordpress/blocks';
import { getBlockAttributes, getBlockContent, getBlockType, isValidBlock, getSaveContent } from '@wordpress/blocks';
import { withSelect, withDispatch } from '@wordpress/data';

class BlockHTML extends Component {
export class BlockHTML extends Component {
constructor( props ) {
super( ...arguments );
this.onChange = this.onChange.bind( this );
Expand All @@ -32,10 +32,20 @@ class BlockHTML extends Component {
}

onBlur() {
const { html } = this.state;
const blockType = getBlockType( this.props.block.name );
const attributes = getBlockAttributes( blockType, this.state.html, this.props.block.attributes );
const isValid = isValidBlock( this.state.html, blockType, attributes );
this.props.onChange( this.props.clientId, attributes, this.state.html, isValid );
const attributes = getBlockAttributes( blockType, html, this.props.block.attributes );

// If html is empty we reset the block to the default HTML and mark it as valid to avoid triggering an error
const content = html ? html : getSaveContent( blockType, attributes );
const isValid = html ? isValidBlock( content, blockType, attributes ) : true;

this.props.onChange( this.props.clientId, attributes, content, isValid );

// Ensure the state is updated if we reset so it displays the default content
if ( ! html ) {
this.setState( { html: content } );
}
}

onChange( event ) {
Expand Down
110 changes: 110 additions & 0 deletions packages/editor/src/components/block-list/test/block-html.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';

/**
* WordPress dependencies
*/
import { createBlock } from '@wordpress/blocks';
import { registerCoreBlocks } from '@wordpress/block-library';

/**
* Internal dependencies
*/
import { BlockHTML } from '../block-html';

describe( 'BlockHTML', () => {
beforeAll( () => {
registerCoreBlocks();
} );

it( 'renders HTML editor', () => {
const wrapper = shallow( <BlockHTML block={ { isValid: true } } /> );

expect( wrapper.name() ).toBe( 'TextareaAutosize' );
} );

describe( 'block content', () => {
it( 'use originalContent for an invalid block', () => {
const wrapper = shallow( <BlockHTML block={ { isValid: false, originalContent: 'test' } } /> );

expect( wrapper.state( 'html' ) ).toBe( 'test' );
} );

it( 'use block content for a valid block', () => {
const block = createBlock( 'core/paragraph', {
content: 'test-block',
isValid: true,
} );

const wrapper = shallow( <BlockHTML block={ block } /> );

expect( wrapper.state( 'html' ) ).toBe( '<p>test-block</p>' );
} );

it( 'onChange updates block html', () => {
const wrapper = shallow( <BlockHTML block={ { isValid: true } } /> );

wrapper.instance().onChange( { target: { value: 'update' } } );

expect( wrapper.state( 'html' ) ).toBe( 'update' );
} );
} );

describe( 'onBlur', () => {
const onChange = jest.fn();

beforeEach( () => {
onChange.mockClear();
} );

it( 'onBlur updates valid HTML string in block', () => {
const block = createBlock( 'core/paragraph', {
content: 'test-block',
isValid: true,
} );
const wrapper = shallow( <BlockHTML block={ block } onChange={ onChange } /> );

wrapper.instance().onChange( { target: { value: '<p>update</p>' } } );
wrapper.instance().onBlur();

expect( onChange ).toHaveBeenCalledTimes( 1 );
expect( onChange.mock.calls[ 0 ][ 2 ] ).toBe( '<p>update</p>' );
expect( onChange.mock.calls[ 0 ][ 3 ] ).toBe( true );
} );

it( 'onBlur updates invalid HTML string in block', () => {
const block = createBlock( 'core/paragraph', {
content: 'test-block',
isValid: true,
} );
const wrapper = shallow( <BlockHTML block={ block } onChange={ onChange } /> );

wrapper.instance().onChange( { target: { value: '<p>update' } } );
wrapper.instance().onBlur();

expect( console ).toHaveErrored();
expect( console ).toHaveWarned();
expect( onChange ).toHaveBeenCalledTimes( 1 );
expect( onChange.mock.calls[ 0 ][ 2 ] ).toBe( '<p>update' );
expect( onChange.mock.calls[ 0 ][ 3 ] ).toBe( false );
} );

it( 'onBlur resets block to default content if supplied empty string', () => {
const block = createBlock( 'core/paragraph', {
content: 'test-block',
isValid: true,
} );
const wrapper = shallow( <BlockHTML block={ block } onChange={ onChange } /> );

wrapper.instance().onChange( { target: { value: '' } } );
wrapper.instance().onBlur();

expect( onChange ).toHaveBeenCalledTimes( 1 );
expect( onChange.mock.calls[ 0 ][ 2 ] ).toBe( '<p></p>' );
expect( onChange.mock.calls[ 0 ][ 3 ] ).toBe( true );
expect( wrapper.state( 'html' ) ).toBe( '<p></p>' );
} );
} );
} );

0 comments on commit 4c579ed

Please sign in to comment.