Skip to content

Block Validation, Deprecation and Migration Experience #7604

Open

Description

image

Individual blocks have a strict shapes of markup and attributes that are expected to consider a block valid within the editor. The way this works, at a fundamental level, is that Gutenberg compares the source given for a ny block with what would be the output if that source is run through the block implementation for that block type (attribute sourcing and save). When there are differences here, we show the already infamous dialog.

It's important to draw a distinction between the validation mechanism and what we choose to do with it (the user experience). For now, it's been incredibly useful to have a very strict validation experience where any detected change is considered an external modification.

The user experience goal, though, should be to avoid bothering users and avoid losing important content. From this, the potential tension is already obvious.

It's also crucial that the validation mechanism remains as simple as possible because it'd have performance costs — it is run for every block to determine validity upfront. So I'm not willing to add overhead there. That said, once we detect a block as initially invalid, that's where we have more room for establishing conditions:

  • We could set up a threshold of characters and be ok with overwriting if it's below it (say 30 characters).
  • We could have a list of elements we allow to be different (attributes like classes).
  • We could overwrite and offer a way back instead of showing a dialog.

These could also be combined for more sophisticated experiences, like attempting our best guess and offer a notice that a block has changed, letting people review the changes if something doesn't seem right. This could also be tied with post revisions. That is, if there's revisions support, and we can save one, we can be more aggressive in making a choice for the user. However, if revisions are not enabled or possible, we might want to be more conservative.

There's also multiple conversion choices we have already implemented:

  • Overwrite: runs the block through save and discards what doesn't match.
  • Convert to HTML: transforms the original block into an HTML block.
  • Convert to Blocks: takes the HTML source and runs it through the raw handling mechanism to infer blocks again.

All of these could be the right choice under certain circumstances, but it's harder to tell which should be the default one. It's important to offer all of these (maybe in an ellipsis menu on the dialog), and be very clear about the wording (the "Edit as HTML" is confusing, for example, since it converts the block).

Also, if these changes are being caused by switching to the HTML editor, we might want to act differently and word things differently — example, don't use "This has been edited externally" and maybe the HTML option becomes "Keep as HTML" in that case.


Previewing

We currently show a dimmed out preview of the block current state. It'd be nice to allow users to check a before / after for the different transformations so they can make the best decision visually. This could also be the basis for a better revisions experience by rendering the different states of the block.


Mockups

Here are some quick drafts as to how the process could look. The specific diffing interface might need refinement as we continue to explore, as well as the phrasings of the various options.

Invalid block message:

invalid

Contrary to now, the message now sits in a box that's the same height of a default paragraph (56px), and a gray scrim extends the height of the block.

Block conversion options:

invalid options

Diffing interface:

diff

Invalidation

Is the process with which a block source is compared with its output before the user interacts with a block. When this fails, for whatever reason, the block is considered invalid. This has been an extremely useful mechanism during the development process, highlighting issues with blocks, plugin compatibilities, and so on.

It's important to clarify that this is not a case of whether the markup is "valid" in terms of being HTML spec-compliant but about how the editor knows to create such markup and that its inability to create an identical result can be a strong indicator of potential data loss (the invalidation is then a protective measure).

It goes without saying that the general expectation for the user experience is that invalidation doesn't happen, and when it does, that it minimizes the amount of user intervention needed. However, considering an invalidation does occur, there are a few cases that need to be separated:

  • Things that should not invalidate a block in the first place (HTML attributes like classes or ids or even data-attributes). Even if they were to be discarded after a save cycle.
  • Things that cannot be reconciled and need a decision (like adding an entirely new paragraph between a figure and img tags within an Image block) given the potential for losing content.
  • The user experience of handling conflicts.

The invalidation process can also be deconstructed in phases:

  1. Validate the block exists.
  2. Validate source matches output.
  3. Validate source matches deprecated outputs.
  4. Validate significance of differences.

These are stacked in a way that favors performance and optimizes for the majority of cases. That is to say, the evaluation logic can become more sophisticated the further down it goes in the process. The first few checks have to be extremely efficient since they will be run for all valid blocks. However, once a block is detected as invalid — failing the three first steps — it is alright to spend more time determining validity before falling back to the user's decision.

Validate significance of differences

This is the area that could use improvements going forwards. Most of the currently reported issues come from differences that should not be significant yet produce an invalidation. There are generally two ways to approach this:

  • Revise block saving to allow for these differences (like HTML attributes).
  • Overwrite differences that fall under a threshold as insignificant.

Related issues:

There are also intricacies that surface once blocks are extended.

Validation based on attributes

It has been proposed in several issues that the validation should be based on the attributes instead of the markup but since the blocks are persisted as markup, this is not something that can be actionable at the moment.

Transformations

Another case of data transformation is present in the mechanism for switching a block to another block type. Transforming a block into another block can be destructive, depending on the heuristics established by the two blocks, the source and the destination. Block transformations also come in two shapes:

  • Registering a transformation for a block into another.
  • Using raw-handling / pasting for conversion.

The first case knows about the block's attributes and is the one used in the main block transformation menu. It allows the most knowledge-transfer in the mapping of attributes. Issues in this conversion should be assigned to the individual blocks responsible for it (example, mapping a quote's cite to a plain paragraph).

The second case is used for extracting blocks out of a Classic block, or converting an HTML block into validated core blocks.

This process is grounded on the same handlers for pasting, which is why in general it removes elements as part of its cleanup process — #6102 —. The intention behind pasting is to clean-up the source without losing meaningful information. However, it could be assumed that given an existing chunk of Classic content the editor could be more lenient in the conversion. One way of handling this is separating both operations, pasting and raw conversion: #6878. Another possibility is to alert the user when something is removed.

Related issues:

Potential Tasks

The aim of this issue is to provide enough context for all these related problems so that any improvements can be discussed holistically. Some examples:

  • Capture unexpected top-level attributes and re-apply them without causing an invalidation.
  • Distinguish between pasting and raw conversion so that different elements can be preserved.
  • Use a visual diff check after a source invalidation.
  • Improve the user experience of handling conflicts: Block Validation, Deprecation and Migration Experience #7604
  • Avoid showing the errors in the console when the block is upgraded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

FrameworkIssues related to broader framework topics, especially as it relates to javascript[Feature] BlocksOverall functionality of blocks[Feature] Paste[Feature] Writing FlowBlock selection, navigation, splitting, merging, deletion...[Type] DiscussionFor issues that are high-level and not yet ready to implement.[Type] OverviewComprehensive, high level view of an area of focus often with multiple tracking issues

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions