Skip to content

Commit

Permalink
Blocks: Remember raw source block for invalid blocks. (#38923)
Browse files Browse the repository at this point in the history
Part of #38922

When the editor is unable to validate a block it should preserve the
broken content in the post and show an accurate representation of that
underlying markup in the absence of being able to interact with it.

Currently when showing a preview of an invalid block in the editor we
attempt to re-generate the save output for a block given the attributes
we originally parsed. This is a flawed approach, however, because by
the nature of being invalid we know that there is a problem with those
attributes as they are.

In this patch we're introducing the `__unstableBlockSource` attribute on 
a block which only exists for invalid blocks at the time of this patch. That 
`__unstableBlockSource` carries the original un-processed data for a block
node and can be used to reconstruct the original markup without using
garbage data and without inadvertently changing it through the series
of autofixes, deprecations, and the like that happen during normal block loading.

The noticable change is in `block-list/block` where we will be showing
that reconstruction rather than the re-generated block content. Previously
it was the case that the preview might represent a corrupted version of the
block or show the block as if emptied of all its content. Now, however,
the preview sould accurately reflect the HTML in the source post even
when it's invalid or unrecognized according to the editor.

Further work should take advantage of the `__unstableBlockSource`
property to provide a more consistent and trusting experience for
working with unrecognized content.
  • Loading branch information
dmsnell authored Mar 14, 2022
1 parent 9b0ed4a commit 3ea2d42
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 11 deletions.
6 changes: 5 additions & 1 deletion packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getBlockType,
getSaveContent,
isUnmodifiedDefaultBlock,
serializeRawBlock,
} from '@wordpress/blocks';
import { withFilters } from '@wordpress/components';
import {
Expand Down Expand Up @@ -74,6 +75,7 @@ function Block( { children, isHtml, ...props } ) {
}

function BlockListBlock( {
block: { __unstableBlockSource },
mode,
isLocked,
canRemove,
Expand Down Expand Up @@ -155,7 +157,9 @@ function BlockListBlock( {
let block;

if ( ! isValid ) {
const saveContent = getSaveContent( blockType, attributes );
const saveContent = __unstableBlockSource
? serializeRawBlock( __unstableBlockSource )
: getSaveContent( blockType, attributes );

block = (
<Block className="has-warning">
Expand Down
24 changes: 24 additions & 0 deletions packages/blocks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,30 @@ _Returns_

- `string`: The post content.

### serializeRawBlock

Serializes a block node into the native HTML-comment-powered block format.
CAVEAT: This function is intended for re-serializing blocks as parsed by
valid parsers and skips any validation steps. This is NOT a generic
serialization function for in-memory blocks. For most purposes, see the
following functions available in the `@wordpress/blocks` package:

_Related_

- serializeBlock
- serialize For more on the format of block nodes as returned by valid parsers:
- `@wordpress/block-serialization-default-parser` package
- `@wordpress/block-serialization-spec-parser` package

_Parameters_

- _rawBlock_ `WPRawBlock`: A block node as returned by a valid parser.
- _options_ `[Options]`: Serialization options.

_Returns_

- `string`: An HTML string representing a block.

### setCategories

Sets the block categories.
Expand Down
1 change: 1 addition & 0 deletions packages/blocks/src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export {
// components whose mechanisms can be shielded from the `edit` implementation
// and just passed along.
export { default as parse } from './parser';
export { serializeRawBlock } from './parser/serialize-raw-block';
export {
getBlockAttributes,
parseWithAttributeSchema,
Expand Down
23 changes: 17 additions & 6 deletions packages/blocks/src/api/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ import { applyBuiltInValidationFixes } from './apply-built-in-validation-fixes';
*
* @typedef WPBlock
*
* @property {string} name Block name
* @property {Object } attributes Block raw or comment attributes.
* @property {WPBlock[]} innerBlocks Inner Blocks.
* @property {string} originalContent Original content of the block before validation fixes.
* @property {boolean} isValid Whether the block is valid.
* @property {Object[]} validationIssues Validation issues.
* @property {string} name Block name
* @property {Object} attributes Block raw or comment attributes.
* @property {WPBlock[]} innerBlocks Inner Blocks.
* @property {string} originalContent Original content of the block before validation fixes.
* @property {boolean} isValid Whether the block is valid.
* @property {Object[]} validationIssues Validation issues.
* @property {WPRawBlock} [__unstableBlockSource] Un-processed original copy of block if created through parser.
*/

/**
Expand Down Expand Up @@ -242,6 +243,16 @@ export function parseRawBlock( rawBlock ) {
blockType
);

if ( ! updatedBlock.isValid ) {
// Preserve the original unprocessed version of the block
// that we received (no fixes, no deprecations) so that
// we can save it as close to exactly the same way as
// we loaded it. This is important to avoid corruption
// and data loss caused by block implementations trying
// to process data that isn't fully recognized.
updatedBlock.__unstableBlockSource = rawBlock;
}

if ( ! validatedBlock.isValid && updatedBlock.isValid ) {
/* eslint-disable no-console */
console.groupCollapsed( 'Updated Block: %s', blockType.name );
Expand Down
14 changes: 10 additions & 4 deletions packages/blocks/src/api/parser/serialize-raw-block.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
*/
import { getCommentDelimitedContent } from '../serializer';

/**
* @typedef {Object} Options Serialization options.
* @property {boolean} [isCommentDelimited=true] Whether to output HTML comments around blocks.
*/

/** @typedef {import("./").WPRawBlock} WPRawBlock */

/**
* Serializes a block node into the native HTML-comment-powered block format.
* CAVEAT: This function is intended for reserializing blocks as parsed by
* CAVEAT: This function is intended for re-serializing blocks as parsed by
* valid parsers and skips any validation steps. This is NOT a generic
* serialization function for in-memory blocks. For most purposes, see the
* following functions available in the `@wordpress/blocks` package:
Expand All @@ -18,9 +25,8 @@ import { getCommentDelimitedContent } from '../serializer';
* @see `@wordpress/block-serialization-default-parser` package
* @see `@wordpress/block-serialization-spec-parser` package
*
* @param {import(".").WPRawBlock} rawBlock A block node as returned by a valid parser.
* @param {?Object} options Serialization options.
* @param {?boolean} options.isCommentDelimited Whether to output HTML comments around blocks.
* @param {WPRawBlock} rawBlock A block node as returned by a valid parser.
* @param {Options} [options={}] Serialization options.
*
* @return {string} An HTML string representing a block.
*/
Expand Down

0 comments on commit 3ea2d42

Please sign in to comment.