Skip to content

Commit

Permalink
Add widget id to blocks in the widgets screen (#28379)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin940726 authored Jan 27, 2021
1 parent d5f58c6 commit 68debe9
Show file tree
Hide file tree
Showing 15 changed files with 377 additions and 141 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* WordPress dependencies
*/
import { registerBlockType } from '@wordpress/blocks';

/**
* External dependencies
*/
Expand All @@ -19,6 +24,15 @@ const TestWrapper = withRegistryProvider( ( props ) => {
} );

describe( 'useBlockSync hook', () => {
beforeAll( () => {
registerBlockType( 'test/test-block', {
title: 'Test block',
attributes: {
foo: { type: 'number' },
},
} );
} );

afterEach( () => {
jest.clearAllMocks();
} );
Expand Down Expand Up @@ -101,7 +115,12 @@ describe( 'useBlockSync hook', () => {
);

const testBlocks = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
{
name: 'test/test-block',
clientId: 'a',
innerBlocks: [],
attributes: { foo: 1 },
},
];
await act( async () => {
root.update(
Expand Down Expand Up @@ -131,7 +150,12 @@ describe( 'useBlockSync hook', () => {
const onInput = jest.fn();

const value1 = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
{
name: 'test/test-block',
clientId: 'a',
innerBlocks: [],
attributes: { foo: 1 },
},
];
let root;
let registry;
Expand Down Expand Up @@ -282,7 +306,12 @@ describe( 'useBlockSync hook', () => {
const onInput = jest.fn();

const value1 = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
{
name: 'test/test-block',
clientId: 'a',
innerBlocks: [],
attributes: { foo: 1 },
},
];

await act( async () => {
Expand Down Expand Up @@ -325,7 +354,12 @@ describe( 'useBlockSync hook', () => {
const onInput = jest.fn();

const value1 = [
{ clientId: 'a', innerBlocks: [], attributes: { foo: 1 } },
{
name: 'test/test-block',
clientId: 'a',
innerBlocks: [],
attributes: { foo: 1 },
},
];

let registry;
Expand All @@ -352,7 +386,14 @@ describe( 'useBlockSync hook', () => {

expect( replaceInnerBlocks ).not.toHaveBeenCalled();
expect( onChange ).toHaveBeenCalledWith(
[ { clientId: 'a', innerBlocks: [], attributes: { foo: 2 } } ],
[
{
name: 'test/test-block',
clientId: 'a',
innerBlocks: [],
attributes: { foo: 2 },
},
],
{ selectionEnd: {}, selectionStart: {} }
);
expect( onInput ).not.toHaveBeenCalled();
Expand Down
12 changes: 3 additions & 9 deletions packages/block-editor/src/components/provider/use-block-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ import { cloneBlock } from '@wordpress/blocks';
* change has been made in the block-editor blocks
* for the given clientId. When this is called,
* controlling sources do not become dirty.
* @param {boolean} props.__unstableCloneValue Whether or not to clone each of
* the blocks provided in the value prop.
*/
export default function useBlockSync( {
clientId = null,
Expand All @@ -73,7 +71,6 @@ export default function useBlockSync( {
selectionEnd: controlledSelectionEnd,
onChange = noop,
onInput = noop,
__unstableCloneValue = true,
} ) {
const registry = useRegistry();

Expand Down Expand Up @@ -101,12 +98,9 @@ export default function useBlockSync( {
if ( clientId ) {
setHasControlledInnerBlocks( clientId, true );
__unstableMarkNextChangeAsNotPersistent();
let storeBlocks = controlledBlocks;
if ( __unstableCloneValue ) {
storeBlocks = controlledBlocks.map( ( block ) =>
cloneBlock( block )
);
}
const storeBlocks = controlledBlocks.map( ( block ) =>
cloneBlock( block )
);
if ( subscribed.current ) {
pendingChanges.current.incoming = storeBlocks;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/blocks/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Breaking Change

- `cloneBlock` now sanitizes the attributes to match the same logic `createBlock` has. [#28379](https://github.com/WordPress/gutenberg/pull/28379)

## 6.25.0 (2020-12-17)

### New Feature
Expand Down
48 changes: 8 additions & 40 deletions packages/blocks/src/api/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import { v4 as uuid } from 'uuid';
import {
every,
reduce,
castArray,
findIndex,
isObjectLike,
Expand All @@ -31,7 +30,7 @@ import {
getBlockTypes,
getGroupingBlockName,
} from './registration';
import { normalizeBlockType } from './utils';
import { normalizeBlockType, sanitizeBlockAttributes } from './utils';

/**
* Returns a block object given its type and attributes.
Expand All @@ -43,40 +42,7 @@ import { normalizeBlockType } from './utils';
* @return {Object} Block object.
*/
export function createBlock( name, attributes = {}, innerBlocks = [] ) {
// Get the type definition associated with a registered block.
const blockType = getBlockType( name );

if ( undefined === blockType ) {
throw new Error( `Block type '${ name }' is not registered.` );
}

// Ensure attributes contains only values defined by block type, and merge
// default values for missing attributes.
const sanitizedAttributes = reduce(
blockType.attributes,
( accumulator, schema, key ) => {
const value = attributes[ key ];

if ( undefined !== value ) {
accumulator[ key ] = value;
} else if ( schema.hasOwnProperty( 'default' ) ) {
accumulator[ key ] = schema.default;
}

if ( [ 'node', 'children' ].indexOf( schema.source ) !== -1 ) {
// Ensure value passed is always an array, which we're expecting in
// the RichText component to handle the deprecated value.
if ( typeof accumulator[ key ] === 'string' ) {
accumulator[ key ] = [ accumulator[ key ] ];
} else if ( ! Array.isArray( accumulator[ key ] ) ) {
accumulator[ key ] = [];
}
}

return accumulator;
},
{}
);
const sanitizedAttributes = sanitizeBlockAttributes( name, attributes );

const clientId = uuid();

Expand Down Expand Up @@ -134,13 +100,15 @@ export function createBlocksFromInnerBlocksTemplate(
export function cloneBlock( block, mergeAttributes = {}, newInnerBlocks ) {
const clientId = uuid();

const sanitizedAttributes = sanitizeBlockAttributes( block.name, {
...block.attributes,
...mergeAttributes,
} );

return {
...block,
clientId,
attributes: {
...block.attributes,
...mergeAttributes,
},
attributes: sanitizedAttributes,
innerBlocks:
newInnerBlocks ||
block.innerBlocks.map( ( innerBlock ) => cloneBlock( innerBlock ) ),
Expand Down
70 changes: 70 additions & 0 deletions packages/blocks/src/api/test/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,23 @@ describe( 'block factory', () => {
content: 'test',
} );
} );

it( 'should sanitize attributes not defined in the block type', () => {
registerBlockType( 'core/test-block', {
...defaultBlockSettings,
attributes: {
align: {
type: 'string',
},
},
} );

const block = createBlock( 'core/test-block', {
notDefined: 'not-defined',
} );

expect( block.attributes ).toEqual( {} );
} );
} );

describe( 'createBlocksFromInnerBlocksTemplate', () => {
Expand Down Expand Up @@ -274,6 +291,31 @@ describe( 'block factory', () => {
type: 'boolean',
default: false,
},
includesDefault: {
type: 'boolean',
default: true,
},
includesFalseyDefault: {
type: 'number',
default: 0,
},
content: {
type: 'array',
source: 'children',
},
defaultContent: {
type: 'array',
source: 'children',
default: 'test',
},
unknownDefaultContent: {
type: 'array',
source: 'children',
default: 1,
},
htmlContent: {
source: 'html',
},
},
save: noop,
category: 'text',
Expand All @@ -287,12 +329,19 @@ describe( 'block factory', () => {

const clonedBlock = cloneBlock( block, {
isDifferent: true,
htmlContent: 'test',
} );

expect( clonedBlock.name ).toEqual( block.name );
expect( clonedBlock.attributes ).toEqual( {
includesDefault: true,
includesFalseyDefault: 0,
align: 'left',
isDifferent: true,
content: [],
defaultContent: [ 'test' ],
unknownDefaultContent: [],
htmlContent: 'test',
} );
expect( clonedBlock.innerBlocks ).toHaveLength( 1 );
expect( typeof clonedBlock.clientId ).toBe( 'string' );
Expand Down Expand Up @@ -375,6 +424,27 @@ describe( 'block factory', () => {
block.innerBlocks[ 1 ].attributes
);
} );

it( 'should sanitize attributes not defined in the block type', () => {
registerBlockType( 'core/test-block', {
...defaultBlockSettings,
attributes: {
align: {
type: 'string',
},
},
} );

const block = createBlock( 'core/test-block', {
notDefined: 'not-defined',
} );

const clonedBlock = cloneBlock( block, {
notDefined2: 'not-defined-2',
} );

expect( clonedBlock.attributes ).toEqual( {} );
} );
} );

describe( 'getPossibleBlockTransformations()', () => {
Expand Down
Loading

0 comments on commit 68debe9

Please sign in to comment.