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

Patterns: Add wrapper to synced patterns and adopt widest alignment of children #54289

Closed
wants to merge 13 commits into from
Closed
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Create and save content to reuse across your site. Update the pattern, and the c

- **Name:** core/block
- **Category:** reusable
- **Supports:** ~~customClassName~~, ~~html~~, ~~inserter~~
- **Supports:** layout (default, ~~allowEditing~~), ~~customClassName~~, ~~html~~, ~~inserter~~
- **Attributes:** ref

## Button
Expand Down
8 changes: 7 additions & 1 deletion packages/block-library/src/block/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
"supports": {
"customClassName": false,
"html": false,
"inserter": false
"inserter": false,
"layout": {
"allowEditing": false,
"default": {
"type": "constrained"
}
}
}
}
19 changes: 19 additions & 0 deletions packages/block-library/src/block/deprecated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Version prior to the addition of a wrapping element for the frontend. No
* attributes changed only the saved markup.
*
* See https://github.com/WordPress/gutenberg/issues/8288
*/
const v1 = {
attributes: { ref: { type: 'number' } },
supports: {
customClassName: false,
html: false,
inserter: false,
},
save: () => null,
};

const deprecated = [ v1 ];

export default deprecated;
54 changes: 52 additions & 2 deletions packages/block-library/src/block/edit.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
Expand All @@ -12,6 +17,7 @@ import {
TextControl,
PanelBody,
} from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import {
useInnerBlocksProps,
Expand All @@ -21,9 +27,22 @@ import {
InspectorControls,
useBlockProps,
Warning,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { useEffect, useState } from '@wordpress/element';

const fullAlignments = [ 'full', 'wide', 'left', 'right' ];

export default function ReusableBlockEdit( {
attributes,
setAttributes,
__unstableParentLayout,
} ) {
const { ref } = attributes;
const [ inferredAlignment, setInferredAlignment ] = useState();

export default function ReusableBlockEdit( { attributes: { ref } } ) {
const { __unstableMarkNextChangeAsNotPersistent } =
useDispatch( blockEditorStore );
const hasAlreadyRendered = useHasRecursion( ref );
const { record, hasResolved } = useEntityRecord(
'postType',
Expand All @@ -45,8 +64,39 @@ export default function ReusableBlockEdit( { attributes: { ref } } ) {
ref
);

useEffect( () => {
// We only track the initial alignment so that if the user could
// initially apply wide or full alignments to inner blocks, that ability
// is maintained even if they temporarily remove the last inner block
// with that wide/full alignment.
if ( ! blocks?.length || inferredAlignment !== undefined ) {
return;
}

const isConstrained = __unstableParentLayout.type === 'constrained';
aaronrobertshaw marked this conversation as resolved.
Show resolved Hide resolved
const hasFullAlignment = blocks.some( ( block ) =>
fullAlignments.includes( block.attributes.align )
);
const alignment = isConstrained && hasFullAlignment ? 'full' : null;

setInferredAlignment( alignment );
__unstableMarkNextChangeAsNotPersistent();
setAttributes( {
layout: alignment ? __unstableParentLayout : undefined,
} );
}, [
blocks,
inferredAlignment,
setAttributes,
__unstableMarkNextChangeAsNotPersistent,
__unstableParentLayout,
] );

const blockProps = useBlockProps( {
className: 'block-library-block__reusable-block-container',
className: classnames(
'block-library-block__reusable-block-container',
{ [ `align${ inferredAlignment }` ]: !! inferredAlignment }
),
} );

const innerBlocksProps = useInnerBlocksProps( blockProps, {
Expand Down
6 changes: 5 additions & 1 deletion packages/block-library/src/block/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@ import { symbol as icon } from '@wordpress/icons';
/**
* Internal dependencies
*/
import deprecated from './deprecated';
import edit from './edit';
import initBlock from '../utils/init-block';
import metadata from './block.json';
import edit from './edit';
import save from './save.js';

const { name } = metadata;

export { metadata, name };

export const settings = {
deprecated,
edit,
icon,
save,
};

export const init = () => initBlock( { name, metadata, settings } );
69 changes: 65 additions & 4 deletions packages/block-library/src/block/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
/**
* Renders the `core/block` block on server.
*
* @param array $attributes The block attributes.
* @param array $attributes The block attributes.
* @param string $block_content The block content.
*
* @return string Rendered HTML of the referenced block.
*/
function render_block_core_block( $attributes ) {
function render_block_core_block( $attributes, $block_content ) {
static $seen_refs = array();

if ( empty( $attributes['ref'] ) ) {
Expand Down Expand Up @@ -41,14 +42,74 @@ function render_block_core_block( $attributes ) {

$seen_refs[ $attributes['ref'] ] = true;

// We need to determine the widest alignment of inner blocks so it can be
// applied to the pattern's wrapper.
$widest_alignment = null;
$filter_alignments = static function( $parsed_block, $source_block, $parent_block ) use ( &$widest_alignment ) {
// If this isn't a top level block in the pattern or we have already
// determined that we have a full aligned block, skip it.
if ( isset( $parent_block ) || 'full' === $widest_alignment ) {
return $parsed_block;
}

$alignment = _wp_array_get( $parsed_block, array( 'attrs', 'align' ), null );
$full_alignments = array( 'full', 'left', 'right' );

if ( in_array( $alignment, $full_alignments, true ) ) {
$widest_alignment = 'full';
return $parsed_block;
}

if ( 'wide' === $alignment ) {
$widest_alignment = $alignment;
}

return $parsed_block;
};

add_filter( 'render_block_data', $filter_alignments, 10, 3 );

// Handle embeds for reusable blocks.
global $wp_embed;

$content = $wp_embed->run_shortcode( $reusable_block->post_content );
$content = $wp_embed->autoembed( $content );

$content = do_blocks( $content );

unset( $seen_refs[ $attributes['ref'] ] );
return $content;

remove_filter( 'render_block_data', $filter_alignments, 10 );

// Older block versions used only the post's content without incorporating
// the editor's wrapper. Newer versions added a wrapper through saved
// markup, so utilize it to return only the post's content when appropriate.
if ( ! $block_content ) {
return $content;
}
aaronrobertshaw marked this conversation as resolved.
Show resolved Hide resolved

$processor = new WP_HTML_Tag_Processor( $block_content );
$processor->next_tag();

// Apply the alignment class to the original block content so its all
// copied across to the result with other classes etc.
if ( null !== $widest_alignment ) {
$processor->add_class( 'align' . $widest_alignment );
}

$tag_name = $processor->get_tag();
$markup = "<$tag_name>$content</$tag_name>";

$merged_content = new WP_HTML_Tag_Processor( $markup );
$merged_content->next_tag();

// Get all the attributes from the original block content and add them to
// the new markup.
$names = $processor->get_attribute_names_with_prefix( '' );
foreach ( $names as $name ) {
$merged_content->set_attribute( $name, $processor->get_attribute( $name ) );
}

return $merged_content->get_updated_html();
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/block-library/src/block/save.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* WordPress dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';

export default function save() {
return <div { ...useBlockProps.save() } />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
exports[`Pattern block transforms to Columns block 1`] = `
"<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column {"width":"100%"} -->
<div class="wp-block-column" style="flex-basis:100%"><!-- wp:block {"ref":130} /--></div>
<div class="wp-block-column" style="flex-basis:100%"><!-- wp:block {"ref":130} -->
<div class="wp-block-block"></div>
<!-- /wp:block --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->"
`;

exports[`Pattern block transforms to Group block 1`] = `
"<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group"><!-- wp:block {"ref":130} /--></div>
<div class="wp-block-group"><!-- wp:block {"ref":130} -->
<div class="wp-block-block"></div>
<!-- /wp:block --></div>
<!-- /wp:group -->"
`;
10 changes: 7 additions & 3 deletions packages/block-library/src/block/test/edit.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ const getMockedReusableBlock = ( id ) => ( {
type: 'wp_block',
} );

const getPatternHtml = ( id ) => `<!-- wp:block {"ref":${ id }} -->
<div class="wp-block-block"></div>
<!-- /wp:block -->`;

beforeAll( () => {
// Register all core blocks.
registerCoreBlocks();
Expand Down Expand Up @@ -118,13 +122,13 @@ describe( 'Synced patterns', () => {
);

expect( reusableBlock ).toBeDefined();
expect( getEditorHtml() ).toBe( '<!-- wp:block {"ref":1} /-->' );
expect( getEditorHtml() ).toBe( getPatternHtml( 1 ) );
} );

it( 'renders warning when the block does not exist', async () => {
// We have to use different ids because entities are cached in memory.
const id = 3;
const initialHtml = `<!-- wp:block {"ref":${ id }} /-->`;
const initialHtml = getPatternHtml( id );

const screen = await initializeEditor( {
initialHtml,
Expand All @@ -145,7 +149,7 @@ describe( 'Synced patterns', () => {
it( 'renders block content', async () => {
// We have to use different ids because entities are cached in memory.
const id = 4;
const initialHtml = `<!-- wp:block {"ref":${ id }} /-->`;
const initialHtml = getPatternHtml( id );
const endpoint = `/wp/v2/blocks/${ id }`;

// Return mocked response for the block endpoint.
Expand Down
4 changes: 3 additions & 1 deletion packages/block-library/src/block/test/transforms.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import {

const block = 'Pattern';
const initialHtml = `
<!-- wp:block {"ref":130} /-->`;
<!-- wp:block {"ref":130} -->
<div class="wp-block-block"></div>
<!-- /wp:block -->`;

const transformsWithInnerBlocks = [ 'Columns', 'Group' ];
const blockTransforms = [ ...transformsWithInnerBlocks ];
Expand Down
4 changes: 3 additions & 1 deletion test/integration/fixtures/blocks/core__block.html
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
<!-- wp:core/block {"ref":123} /-->
<!-- wp:core/block {"ref":123} -->
<div class="wp-block-block"></div>
<!-- /wp:core/block -->
4 changes: 2 additions & 2 deletions test/integration/fixtures/blocks/core__block.parsed.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"ref": 123
},
"innerBlocks": [],
"innerHTML": "",
"innerContent": []
"innerHTML": "\n<div class=\"wp-block-block\"></div>\n",
"innerContent": [ "\n<div class=\"wp-block-block\"></div>\n" ]
}
]
4 changes: 3 additions & 1 deletion test/integration/fixtures/blocks/core__block.serialized.html
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
<!-- wp:block {"ref":123} /-->
<!-- wp:block {"ref":123} -->
<div class="wp-block-block"></div>
<!-- /wp:block -->
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- wp:core/block {"ref":123} /-->
10 changes: 10 additions & 0 deletions test/integration/fixtures/blocks/core__block__deprecated-v0.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"name": "core/block",
"isValid": true,
"attributes": {
"ref": 123
},
"innerBlocks": []
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"blockName": "core/block",
"attrs": {
"ref": 123
},
"innerBlocks": [],
"innerHTML": "",
"innerContent": []
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- wp:block {"ref":123} -->
<div class="wp-block-block"></div>
<!-- /wp:block -->