Skip to content

Commit

Permalink
Editor: Shim meta attributes for early block registrations (#20544)
Browse files Browse the repository at this point in the history
* Editor: Shim meta attributes for early block registrations

* E2E Tests: Add meta block early registration test

* Editor: Clarify comment regarding meta attribute shim
  • Loading branch information
aduth authored and jorgefilipecosta committed Mar 2, 2020
1 parent 6fd7289 commit 3feaa84
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 96 deletions.
16 changes: 13 additions & 3 deletions packages/e2e-tests/plugins/meta-attribute-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,23 @@ function init_test_meta_attribute_block_plugin() {
*/
function enqueue_test_meta_attribute_block() {
wp_enqueue_script(
'gutenberg-test-meta-attribute-block',
plugins_url( 'meta-attribute-block/index.js', __FILE__ ),
'gutenberg-test-meta-attribute-block-early',
plugins_url( 'meta-attribute-block/early.js', __FILE__ ),
array(
'wp-blocks',
'wp-element',
),
filemtime( plugin_dir_path( __FILE__ ) . 'meta-attribute-block/index.js' ),
filemtime( plugin_dir_path( __FILE__ ) . 'meta-attribute-block/early.js' )
);

wp_enqueue_script(
'gutenberg-test-meta-attribute-block-late',
plugins_url( 'meta-attribute-block/late.js', __FILE__ ),
array(
'wp-blocks',
'wp-element',
),
filemtime( plugin_dir_path( __FILE__ ) . 'meta-attribute-block/late.js' ),
true
);
}
Expand Down
32 changes: 32 additions & 0 deletions packages/e2e-tests/plugins/meta-attribute-block/early.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
( function() {
var registerBlockType = wp.blocks.registerBlockType;
var el = wp.element.createElement;

registerBlockType( 'test/test-meta-attribute-block-early', {
title: 'Test Meta Attribute Block (Early Registration)',
icon: 'star',
category: 'common',

attributes: {
content: {
type: 'string',
source: 'meta',
meta: 'my_meta',
},
},

edit: function( props ) {
return el( 'input', {
className: 'my-meta-input',
value: props.attributes.content,
onChange: function( event ) {
props.setAttributes( { content: event.target.value } );
},
} );
},

save: function() {
return null;
},
} );
} )();
35 changes: 0 additions & 35 deletions packages/e2e-tests/plugins/meta-attribute-block/index.js

This file was deleted.

32 changes: 32 additions & 0 deletions packages/e2e-tests/plugins/meta-attribute-block/late.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
( function() {
var registerBlockType = wp.blocks.registerBlockType;
var el = wp.element.createElement;

registerBlockType( 'test/test-meta-attribute-block-late', {
title: 'Test Meta Attribute Block (Late Registration)',
icon: 'star',
category: 'common',

attributes: {
content: {
type: 'string',
source: 'meta',
meta: 'my_meta',
},
},

edit: function( props ) {
return el( 'input', {
className: 'my-meta-input',
value: props.attributes.content,
onChange: function( event ) {
props.setAttributes( { content: event.target.value } );
},
} );
},

save: function() {
return null;
},
} );
} )();
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Block with a meta attribute Should persist the meta attribute properly 1`] = `"<!-- wp:test/test-meta-attribute-block /-->"`;
exports[`Block with a meta attribute Early Registration Should persist the meta attribute properly 1`] = `"<!-- wp:test/test-meta-attribute-block-early /-->"`;

exports[`Block with a meta attribute Should persist the meta attribute properly in a different post type 1`] = `"<!-- wp:test/test-meta-attribute-block /-->"`;
exports[`Block with a meta attribute Early Registration Should persist the meta attribute properly in a different post type 1`] = `"<!-- wp:test/test-meta-attribute-block-early /-->"`;

exports[`Block with a meta attribute Late Registration Should persist the meta attribute properly 1`] = `"<!-- wp:test/test-meta-attribute-block-late /-->"`;

exports[`Block with a meta attribute Late Registration Should persist the meta attribute properly in a different post type 1`] = `"<!-- wp:test/test-meta-attribute-block-late /-->"`;
117 changes: 62 additions & 55 deletions packages/e2e-tests/specs/editor/plugins/meta-attribute-block.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,68 +24,75 @@ describe( 'Block with a meta attribute', () => {
await deactivatePlugin( 'gutenberg-test-meta-attribute-block' );
} );

it( 'Should persist the meta attribute properly', async () => {
await insertBlock( 'Test Meta Attribute Block' );
await page.keyboard.type( 'Value' );
describe.each( [ [ 'Early Registration' ], [ 'Late Registration' ] ] )(
'%s',
( variant ) => {
it( 'Should persist the meta attribute properly', async () => {
await insertBlock( `Test Meta Attribute Block (${ variant })` );
await page.keyboard.type( 'Value' );

// Regression Test: Previously the caret would wrongly reset to the end
// of any input for meta-sourced attributes, due to syncing behavior of
// meta attribute updates.
//
// See: https://github.com/WordPress/gutenberg/issues/15739
await pressKeyTimes( 'ArrowLeft', 5 );
await page.keyboard.type( 'Meta ' );
// Regression Test: Previously the caret would wrongly reset to the end
// of any input for meta-sourced attributes, due to syncing behavior of
// meta attribute updates.
//
// See: https://github.com/WordPress/gutenberg/issues/15739
await pressKeyTimes( 'ArrowLeft', 5 );
await page.keyboard.type( 'Meta ' );

await saveDraft();
await page.reload();
await saveDraft();
await page.reload();

expect( await getEditedPostContent() ).toMatchSnapshot();
const persistedValue = await page.evaluate(
() => document.querySelector( '.my-meta-input' ).value
);
expect( persistedValue ).toBe( 'Meta Value' );
} );
expect( await getEditedPostContent() ).toMatchSnapshot();
const persistedValue = await page.evaluate(
() => document.querySelector( '.my-meta-input' ).value
);
expect( persistedValue ).toBe( 'Meta Value' );
} );

it( 'Should use the same value in all the blocks', async () => {
await insertBlock( 'Test Meta Attribute Block' );
await insertBlock( 'Test Meta Attribute Block' );
await insertBlock( 'Test Meta Attribute Block' );
await page.keyboard.type( 'Meta Value' );
it( 'Should use the same value in all the blocks', async () => {
await insertBlock( `Test Meta Attribute Block (${ variant })` );
await insertBlock( `Test Meta Attribute Block (${ variant })` );
await insertBlock( `Test Meta Attribute Block (${ variant })` );
await page.keyboard.type( 'Meta Value' );

const inputs = await page.$$( '.my-meta-input' );
await Promise.all(
inputs.map( async ( input ) => {
// Clicking the input selects the block,
// and selecting the block enables the sync data mode
// as otherwise the asynchronous re-rendering of unselected blocks
// may cause the input to have not yet been updated for the other blocks
await input.click();
const inputValue = await input.getProperty( 'value' );
expect( await inputValue.jsonValue() ).toBe( 'Meta Value' );
} )
);
} );
const inputs = await page.$$( '.my-meta-input' );
await Promise.all(
inputs.map( async ( input ) => {
// Clicking the input selects the block,
// and selecting the block enables the sync data mode
// as otherwise the asynchronous re-rendering of unselected blocks
// may cause the input to have not yet been updated for the other blocks
await input.click();
const inputValue = await input.getProperty( 'value' );
expect( await inputValue.jsonValue() ).toBe(
'Meta Value'
);
} )
);
} );

it( 'Should persist the meta attribute properly in a different post type', async () => {
await createNewPost( { postType: 'page' } );
await insertBlock( 'Test Meta Attribute Block' );
await page.keyboard.type( 'Value' );
it( 'Should persist the meta attribute properly in a different post type', async () => {
await createNewPost( { postType: 'page' } );
await insertBlock( `Test Meta Attribute Block (${ variant })` );
await page.keyboard.type( 'Value' );

// Regression Test: Previously the caret would wrongly reset to the end
// of any input for meta-sourced attributes, due to syncing behavior of
// meta attribute updates.
//
// See: https://github.com/WordPress/gutenberg/issues/15739
await pressKeyTimes( 'ArrowLeft', 5 );
await page.keyboard.type( 'Meta ' );
// Regression Test: Previously the caret would wrongly reset to the end
// of any input for meta-sourced attributes, due to syncing behavior of
// meta attribute updates.
//
// See: https://github.com/WordPress/gutenberg/issues/15739
await pressKeyTimes( 'ArrowLeft', 5 );
await page.keyboard.type( 'Meta ' );

await saveDraft();
await page.reload();
await saveDraft();
await page.reload();

expect( await getEditedPostContent() ).toMatchSnapshot();
const persistedValue = await page.evaluate(
() => document.querySelector( '.my-meta-input' ).value
);
expect( persistedValue ).toBe( 'Meta Value' );
} );
expect( await getEditedPostContent() ).toMatchSnapshot();
const persistedValue = await page.evaluate(
() => document.querySelector( '.my-meta-input' ).value
);
expect( persistedValue ).toBe( 'Meta Value' );
} );
}
);
} );
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { pickBy, mapValues, isEmpty, mapKeys } from 'lodash';
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { select as globalSelect, useSelect } from '@wordpress/data';
import { useEntityProp } from '@wordpress/core-data';
import { useMemo } from '@wordpress/element';
import { createHigherOrderComponent } from '@wordpress/compose';
Expand Down Expand Up @@ -116,3 +116,26 @@ addFilter(
'core/editor/custom-sources-backwards-compatibility/shim-attribute-source',
shimAttributeSource
);

// The above filter will only capture blocks registered after the filter was
// added. There may already be blocks registered by this point, and those must
// be updated to apply the shim.
//
// The following implementation achieves this, albeit with a couple caveats:
// - Only blocks registered on the global store will be modified.
// - The block settings are directly mutated, since there is currently no
// mechanism to update an existing block registration. This is the reason for
// `getBlockType` separate from `getBlockTypes`, since the latter returns a
// _copy_ of the block registration (i.e. the mutation would not affect the
// actual registered block settings).
//
// `getBlockTypes` or `getBlockType` implementation could change in the future
// in regards to creating settings clones, but the corresponding end-to-end
// tests for meta blocks should cover against any potential regressions.
//
// In the future, we could support updating block settings, at which point this
// implementation could use that mechanism instead.
globalSelect( 'core/blocks' )
.getBlockTypes()
.map( ( { name } ) => globalSelect( 'core/blocks' ).getBlockType( name ) )
.forEach( shimAttributeSource );

0 comments on commit 3feaa84

Please sign in to comment.