Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try: Fix issue where block fails validation when a default attribute is deprecated #12757

Merged
merged 3 commits into from
Feb 19, 2019

Conversation

talldan
Copy link
Contributor

@talldan talldan commented Dec 10, 2018

Description

Fixes #11705

Block implementors are unable update their code to change the default value for an attribute, even when adding a deprecation. Block validation fails for posts that contain blocks still using an old default.

Note - this is only for attributes that aren't sourced

How has this been tested?

  1. Add a columns block to a new post (keeping the number of columns at the default of 2) and save the post
  2. Modify the code of the columns block, changing the columns attribute default from 2 to 3.
  3. Load the post created in step 1 and observe that the columns block failed validation.
  4. Add a deprecation for the columns block to try to solve the failed validation, ensure this new deprecation still has the old default value for the columns attribute of 2.
  5. Load the post created in step 1.

Expected Result
The deprecation with the column count of 2 should be applied.

Actual Result
The block fails validation.

Diagnosis

The bug occurs because the attempt to apply the deprecation fails validation here and so the deprecation is skipped (isValid is false):

let migratedAttributes = getBlockAttributes(
deprecatedBlockType,
originalContent,
attributes
);
// Ignore the deprecation if it produces a block which is not valid.
const isValid = isValidBlockContent(
deprecatedBlockType,
migratedAttributes,
originalContent
);
if ( ! isValid ) {
continue;
}

This is happening because the attributes variable in the above call to getBlockAttributes has the default value from the failed initial attempt to create the block (in case of the columns example above, that would be the new default of 3). Because of that the deprecation's default is not applied (as there's already an existing value).

Fix

Instead of using the attributes from the failed first attempt to create the block when validating a deprecation, this fix instead uses the attributes as they were when the block markup was parsed (those initially passed into createBlockWithFallback):

export function createBlockWithFallback( blockNode ) {
const { blockName: originalName } = blockNode;
let {
attrs: attributes,
innerBlocks = [],
innerHTML,
} = blockNode;

This means that a deprecation receives the same attributes that normal block creation would receive, which to me seems logical.

Types of changes

Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.

@talldan talldan added [Type] Bug An existing feature does not function as intended [Feature] Block API API that allows to express the block paradigm. [Package] Blocks /packages/blocks labels Dec 10, 2018
@talldan talldan self-assigned this Dec 10, 2018
@talldan talldan requested a review from a team December 10, 2018 11:01
@aduth
Copy link
Member

aduth commented Dec 10, 2018

There appear to be some legitimate test failures here.

@talldan
Copy link
Contributor Author

talldan commented Dec 11, 2018

Oh, sorry about that, updated tests and added a test case to cover this fix.

@talldan talldan added the Needs Technical Feedback Needs testing from a developer perspective. label Jan 2, 2019
@talldan
Copy link
Contributor Author

talldan commented Jan 2, 2019

Would like some developer feedback on this. I realise this introduces the need to deprecate getMigratedBlock due to the extra param, which is less than ideal.

One way around that could be to store the original attributes on the block in the same way as originalContent is here before the call to getMigratedBlock:

block.originalContent = innerHTML;
block = getMigratedBlock( block );

I wanted to avoid mutation and that data being kept around longer than necessary.

Also, let me know if I need to clarify anything about this bug, it's not the easiest issue to explain.

@gziolo gziolo added this to the 5.2 (Gutenberg) milestone Feb 6, 2019
@aduth
Copy link
Member

aduth commented Feb 6, 2019

I realise this introduces the need to deprecate getMigratedBlock due to the extra param, which is less than ideal.

Can you elaborate why a deprecation would occur, if getMigratedBlock is not part of any public interface?


expect( migratedBlock.attributes ).toEqual( { fruit: 'Bananas!' } );
} );

it( 'allows a default attribute to be deprecated', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being a bit confused about what actually changed as part of the implementation aside from moving attributes into an argument rather than having it be destructured, I copied this test and this test only into master. It passed. I can't imagine that's expected? I'd expect the test case would be one which only passes in the branch, and fails on master.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch - looks like I made a mistake in the test with attribute type, which was causing a false positive (the types didn't match so that attribute was being reset). Fixed here:
ec0e490

Being a bit confused about what actually changed as part of the implementation aside from moving attributes into an argument rather than having it be destructured.

It's really hard to explain!

The difference is that before this change, the attributes may have had defaults applied from the main block type:
https://github.com/WordPress/gutenberg/blob/master/packages/blocks/src/api/parser.js#L455

If the main block type fails validation, the deprecations are attempted, but the default value from main block type is passed in when testing the deprecations, which doesn't give the deprecations a chance to apply their own defaults.

My change instead passes the original attributes from before those defaults from the main block type have been applied into getMigratedBlock, which gives the deprecations a chance to use their defaults.

@talldan
Copy link
Contributor Author

talldan commented Feb 8, 2019

Can you elaborate why a deprecation would occur, if getMigratedBlock is not part of any public interface?

I think that's down to me misinterpreting what's public and what's internal. Thanks for the clarification.

@talldan talldan force-pushed the try/fix-default-attribute-deprecation branch from 059edef to ec0e490 Compare February 8, 2019 08:44
Copy link
Contributor

@mcsf mcsf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change is well reasoned and tests well for me. Thanks for the fix, LGTM.

@aduth, did Dan clarify things for you?

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation 👍

@talldan talldan removed the Needs Technical Feedback Needs testing from a developer perspective. label Feb 19, 2019
@talldan talldan merged commit 0254aaf into master Feb 19, 2019
@talldan talldan deleted the try/fix-default-attribute-deprecation branch February 19, 2019 09:02
youknowriad pushed a commit that referenced this pull request Mar 6, 2019
…is deprecated (#12757)

* Use attributes as parsed when determining a block migration, not the attributes from the failed created block

* Updated tests with new migratedBlocks signature and add new test to cover deprecated default use case

* Fix typo with attribute type in test
youknowriad pushed a commit that referenced this pull request Mar 6, 2019
…is deprecated (#12757)

* Use attributes as parsed when determining a block migration, not the attributes from the failed created block

* Updated tests with new migratedBlocks signature and add new test to cover deprecated default use case

* Fix typo with attribute type in test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block API API that allows to express the block paradigm. [Package] Blocks /packages/blocks [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error when deprecating the default value of a block attribute
4 participants