Skip to content

Commit

Permalink
Add UI for unregistered block types
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonpayton committed Aug 2, 2018
1 parent a975273 commit d276d2d
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 17 deletions.
9 changes: 7 additions & 2 deletions core-blocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import {
registerBlockType,
setDefaultBlockName,
setUnknownTypeHandlerName,
setNonblockHandlerName,
setUnregisteredTypeHandlerName,
} from '@wordpress/blocks';

/**
Expand All @@ -31,6 +32,7 @@ import * as html from './html';
import * as latestComments from './latest-comments';
import * as latestPosts from './latest-posts';
import * as list from './list';
import * as missing from './missing';
import * as more from './more';
import * as nextpage from './nextpage';
import * as preformatted from './preformatted';
Expand Down Expand Up @@ -74,6 +76,7 @@ export const registerCoreBlocks = () => {
html,
latestComments,
latestPosts,
missing,
more,
nextpage,
preformatted,
Expand All @@ -91,5 +94,7 @@ export const registerCoreBlocks = () => {
} );

setDefaultBlockName( paragraph.name );
setUnknownTypeHandlerName( freeform.name );
setNonblockHandlerName( freeform.name );
// TODO: Consider renaming "Unregistered" to missing
setUnregisteredTypeHandlerName( missing.name );
};
5 changes: 5 additions & 0 deletions core-blocks/missing/editor.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.editor-block-list__block[data-type="core/missing"] {
.editor-warning {
position: static;
}
}
39 changes: 39 additions & 0 deletions core-blocks/missing/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { RawHTML } from '@wordpress/element';

/**
* Internal dependencies.
*/
import MissingBlockWarning from './missing-block-warning';
import './editor.scss';

export const name = 'core/missing';

export const settings = {
name,
category: 'common',
title: __( 'Missing Block' ),

supports: {
className: false,
customClassName: false,
inserter: false,
html: false,
preserveOriginalContent: true,
},

attributes: {
originalContent: {
type: 'string',
source: 'html',
},
},

edit: MissingBlockWarning,
save( { attributes } ) {
return <RawHTML>{ attributes.originalContent }</RawHTML>;
},
};
61 changes: 61 additions & 0 deletions core-blocks/missing/missing-block-warning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { getBlockType, createBlock } from '@wordpress/blocks';
import { withSelect, withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { Warning } from '@wordpress/editor';

export const name = 'core/unknown';

export function MissingBlockWarning( { block, convertToHTML } ) {
const hasContent = !! block.originalUndelimitedContent;
const hasHTMLBlock = getBlockType( 'core/html' );

const actions = [];
let messageHTML;
if ( hasContent && hasHTMLBlock ) {
actions.push(
<Button key="convert" onClick={ convertToHTML } isLarge isPrimary>
{ __( 'HTML Block' ) }
</Button>
);
messageHTML = sprintf(
__( 'Your site doesn\'t include support for the <code>%s</code> block. You can leave the block intact, convert its content to a Custom HTML block, or remove it entirely.' ),
block.originalName
);
} else {
messageHTML = sprintf(
__( 'Your site doesn\'t include support for the <code>%s</code> block. You can leave the block intact or remove it entirely.' ),
block.originalName
);
}

return (
<Warning actions={ actions }>
<span dangerouslySetInnerHTML={ { __html: messageHTML } } />
</Warning>
);
}

export default compose( [
withSelect( ( select, { clientId } ) => {
const { getBlock } = select( 'core/editor' );
return {
block: getBlock( clientId ),
};
} ),
withDispatch( ( dispatch, { block } ) => {
const { replaceBlock } = dispatch( 'core/editor' );
return {
convertToHTML() {
replaceBlock( block.clientId, createBlock( 'core/html', {
content: block.originalUndelimitedContent,
} ) );
},
};
} ),
] )( MissingBlockWarning );

4 changes: 4 additions & 0 deletions packages/blocks/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export {
unregisterBlockType,
setUnknownTypeHandlerName,
getUnknownTypeHandlerName,
setNonblockHandlerName,
getNonblockHandlerName,
setUnregisteredTypeHandlerName,
getUnregisteredTypeHandlerName,
setDefaultBlockName,
getDefaultBlockName,
getDefaultBlockForPostFormat,
Expand Down
42 changes: 29 additions & 13 deletions packages/blocks/src/api/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import { parse as grammarParse } from '@wordpress/block-serialization-spec-parse
/**
* Internal dependencies
*/
import { getBlockType, getUnknownTypeHandlerName } from './registration';
import {
getBlockType,
getUnknownTypeHandlerName,
getNonblockHandlerName,
getUnregisteredTypeHandlerName,
} from './registration';
import { createBlock } from './factory';
import { isValidBlock } from './validation';
import { getCommentDelimitedContent } from './serializer';
Expand Down Expand Up @@ -284,54 +289,59 @@ export function getMigratedBlock( block ) {
* @return {?Object} An initialized block object (if possible).
*/
export function createBlockWithFallback( blockNode ) {
const { blockName: originalName } = blockNode;
let {
blockName: name,
attrs: attributes,
innerBlocks = [],
innerHTML,
} = blockNode;
const fallbackBlock = getUnknownTypeHandlerName();
const nonblockFallbackBlock = getNonblockHandlerName();
const unregisteredFallbackBlock = getUnregisteredTypeHandlerName();

attributes = attributes || {};

// Trim content to avoid creation of intermediary freeform segments.
innerHTML = innerHTML.trim();
const originalUndelimitedContent = innerHTML = innerHTML.trim();

// Use type from block content, otherwise find unknown handler.
name = name || getUnknownTypeHandlerName();
let name = originalName || nonblockFallbackBlock || fallbackBlock;

// Convert 'core/text' blocks in existing content to 'core/paragraph'.
if ( 'core/text' === name || 'core/cover-text' === name ) {
name = 'core/paragraph';
}

// Try finding the type for known block name, else fall back again.
let blockType = getBlockType( name );

const fallbackBlock = getUnknownTypeHandlerName();

// Fallback content may be upgraded from classic editor expecting implicit
// automatic paragraphs, so preserve them. Assumes wpautop is idempotent,
// meaning there are no negative consequences to repeated autop calls.
if ( name === fallbackBlock ) {
if ( name === nonblockFallbackBlock || name === fallbackBlock ) {
innerHTML = autop( innerHTML ).trim();
}

// Try finding the type for known block name, else fall back again.
let blockType = getBlockType( name );

if ( ! blockType ) {
// If detected as a block which is not registered, preserve comment
// delimiters in content of unknown type handler.
// delimiters in content of missing type handler.
if ( name ) {
innerHTML = getCommentDelimitedContent( name, attributes, innerHTML );
}

name = fallbackBlock;
name = unregisteredFallbackBlock || fallbackBlock;
attributes = {};
blockType = getBlockType( name );
}

// Coerce inner blocks from parsed form to canonical form.
innerBlocks = innerBlocks.map( createBlockWithFallback );

// Include in set only if type were determined.
if ( ! blockType || ( ! innerHTML && name === fallbackBlock ) ) {
if (
! blockType ||
( ! innerHTML && ( name === nonblockFallbackBlock || name === fallbackBlock ) )
) {
return;
}

Expand All @@ -349,6 +359,12 @@ export function createBlockWithFallback( blockNode ) {
block.isValid = isValidBlock( innerHTML, blockType, block.attributes );
}

// TODO: See if there is a better way to pass this information.
if ( name === unregisteredFallbackBlock ) {
block.originalName = originalName;
block.originalUndelimitedContent = originalUndelimitedContent;
}

// Preserve original content for future use in case the block is parsed as
// invalid, or future serialization attempt results in an error.
block.originalContent = innerHTML;
Expand Down
43 changes: 43 additions & 0 deletions packages/blocks/src/api/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ export function unregisterBlockType( name ) {
* @param {string} name Block name.
*/
export function setUnknownTypeHandlerName( name ) {
deprecated( 'setUnknownTypeHandlerName', {
plugin: 'Gutenberg',
version: '3.7',
alternative: 'setNonblockHandlerName and setUnregisteredTypeHandlerName',
} );
dispatch( 'core/blocks' ).setFallbackBlockName( name );
}

Expand All @@ -211,6 +216,44 @@ export function getUnknownTypeHandlerName() {
return select( 'core/blocks' ).getFallbackBlockName();
}

/**
* Assigns name of block handling unknown block types.
*
* @param {string} name Block name.
*/
export function setNonblockHandlerName( name ) {
dispatch( 'core/blocks' ).setNonblockFallbackBlockName( name );
}

/**
* Retrieves name of block handling unknown block types, or undefined if no
* handler has been defined.
*
* @return {?string} Blog name.
*/
export function getNonblockHandlerName() {
return select( 'core/blocks' ).getNonblockFallbackBlockName();
}

/**
* Assigns name of block handling unknown block types.
*
* @param {string} name Block name.
*/
export function setUnregisteredTypeHandlerName( name ) {
dispatch( 'core/blocks' ).setUnregisteredFallbackBlockName( name );
}

/**
* Retrieves name of block handling unknown block types, or undefined if no
* handler has been defined.
*
* @return {?string} Blog name.
*/
export function getUnregisteredTypeHandlerName() {
return select( 'core/blocks' ).getUnregisteredFallbackBlockName();
}

/**
* Assigns the default block name.
*
Expand Down
11 changes: 9 additions & 2 deletions packages/blocks/src/api/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import isShallowEqual from '@wordpress/is-shallow-equal';
/**
* Internal dependencies
*/
import { getBlockType, getUnknownTypeHandlerName } from './registration';
import {
getBlockType,
getUnknownTypeHandlerName,
getNonblockHandlerName,
getUnregisteredTypeHandlerName,
} from './registration';
import BlockContentProvider from '../block-content-provider';

/**
Expand Down Expand Up @@ -205,7 +210,7 @@ export function getBlockContent( block ) {
// otherwise have no access to its original content and content loss would
// still occur.
let saveContent = block.originalContent;
if ( block.isValid || block.innerBlocks.length ) {
if ( blockType && ( block.isValid || block.innerBlocks.length ) ) {
try {
saveContent = getSaveContent( blockType, block.attributes, block.innerBlocks );
} catch ( error ) {}
Expand Down Expand Up @@ -261,6 +266,8 @@ export function serializeBlock( block ) {
const saveAttributes = getCommentAttributes( block.attributes, blockType );

switch ( blockName ) {
case getNonblockHandlerName():
case getUnregisteredTypeHandlerName():
case getUnknownTypeHandlerName():
return saveContent;

Expand Down
Loading

0 comments on commit d276d2d

Please sign in to comment.