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

Pattern: Experiment with using template slots for content replacement #50777

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,15 @@ A cloud of your most used tags. ([Source](https://github.com/WordPress/gutenberg
- **Supports:** align, anchor, spacing (margin, padding), typography (lineHeight), ~~html~~
- **Attributes:** largestFontSize, numberOfTags, showTagCounts, smallestFontSize, taxonomy

## Template Content

Use template content to replace the template placeholder. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/template-content))

- **Name:** core/template-content
- **Category:** widgets
- **Supports:** ~~className~~, ~~customClassName~~, ~~html~~, ~~inserter~~
- **Attributes:** content, name

## Template Part

Edit the different global regions of your site, like the header, footer, sidebar, or create your own. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/template-part))
Expand Down
1 change: 1 addition & 0 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function gutenberg_reregister_core_block_types() {
'spacer',
'table',
'table-of-contents',
'template-content',
'text-columns',
'verse',
'video',
Expand Down
42 changes: 42 additions & 0 deletions lib/experimental/pattern.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
/**
* Temporary compatibility shims for pattern APIs present in Gutenberg.
*
* @package gutenberg
*/

register_block_pattern(
'gutenberg/get-in-touch',
array(
'title' => esc_html__( 'Get In Touch', 'default' ),
'categories' => array( 'call-to-action' ),
'content' => implode(
'',
array(
'<!-- wp:paragraph {"fontSize":"huge"} -->',
'<p class="has-huge-font-size"></$wp:template-content name="getInTouch"></p>',
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the / in the right place for $wp:template-content?

Copy link
Member Author

@gziolo gziolo May 24, 2023

Choose a reason for hiding this comment

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

I was experimenting with the idea of the tag closer that gets converted to the HTML comment in the browser. In effect, if you never replace </$wp:template-content name="getInTouch"> with the content, the block will still get rendered correctly for the site editors because it will simply skip this syntax for the content placeholder.

@dmsnell came up with the idea of using something like </$wp:template-content>. The interesting aspect of it is that it can be matched with the HTML tag processor when visiting closing HTML tags.

'<!-- /wp:paragraph -->',
'<!-- wp:columns -->',
'<div class="wp-block-columns"><!-- wp:column -->',
'<div class="wp-block-column"><!-- wp:paragraph -->',
'<p>' . esc_html__( '20 Cooper Avenue', 'default' ) . '<br>' . esc_html__( 'New York, New York 10023', 'default' ) . '</p>',
'<!-- /wp:paragraph --></div>',
'<!-- /wp:column -->',
'<!-- wp:column -->',
'<div class="wp-block-column"><!-- wp:paragraph -->',
'<p>' . esc_html__( '(555) 555-5555', 'default' ) . '<br><a href="mailto:example@example.com">' . esc_html__( 'example@example.com', 'default' ) . '</a></p>',
'<!-- /wp:paragraph --></div>',
'<!-- /wp:column --></div>',
'<!-- /wp:columns -->',
'<!-- wp:buttons -->',
'<div class="wp-block-buttons"><!-- wp:button {"backgroundColor":"dark-gray"} -->',
'<div class="wp-block-button"><a class="wp-block-button__link has-dark-gray-background-color has-background">' . esc_html__( 'Contact Us', 'default' ) . '</a></div>',
'<!-- /wp:button --></div>',
'<!-- /wp:buttons -->',
'<!-- wp:template-content {"name":"getInTouch"} -->',
esc_html__( 'Get In Touch', 'default' ),
'<!-- /wp:template-content -->',
)
),
)
);
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/kses.php';
require __DIR__ . '/experimental/l10n.php';
require __DIR__ . '/experimental/navigation-fallback.php';
require __DIR__ . '/experimental/pattern.php';
if ( gutenberg_is_experiment_enabled( 'gutenberg-interactivity-api-core-blocks' ) ) {
require __DIR__ . '/experimental/interactivity-api/script-loader.php';
require __DIR__ . '/experimental/interactivity-api/blocks.php';
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export {
RichTextToolbarButton,
__unstableRichTextInputEvent,
} from './rich-text';
export { default as __experimentalTemplateContent } from './template-content';
export { default as ToolSelector } from './tool-selector';
export { default as __experimentalUnitControl } from './unit-control';
export { default as URLInput } from './url-input';
Expand Down
14 changes: 13 additions & 1 deletion packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { useInsertReplacementText } from './use-insert-replacement-text';
import { useFirefoxCompat } from './use-firefox-compat';
import FormatEdit from './format-edit';
import { getMultilineTag, getAllowedFormats } from './utils';
import TemplateContent from '../template-content';

export const keyboardShortcutContext = createContext();
export const inputEventContext = createContext();
Expand Down Expand Up @@ -318,7 +319,7 @@ function RichTextWrapper(
}

const TagName = tagName;
return (
const result = (
<>
{ isSelected && (
<keyboardShortcutContext.Provider value={ keyboardShortcuts }>
Expand Down Expand Up @@ -415,6 +416,17 @@ function RichTextWrapper(
/>
</>
);

if ( ! originalValue.startsWith( '<!--$wp:template-content' ) ) {
return result;
}

return (
<div { ...props }>
<TemplateContent.Slot bubblesVirtually />
Hello!
</div>
);
}

const ForwardedRichTextContainer = forwardRef( RichTextWrapper );
Expand Down
10 changes: 10 additions & 0 deletions packages/block-editor/src/components/template-content/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* WordPress dependencies
*/
import { createSlotFill } from '@wordpress/components';

const { Fill: TemplateContent, Slot } = createSlotFill( 'TemplateContent' );

TemplateContent.Slot = Slot;

export default TemplateContent;
2 changes: 2 additions & 0 deletions packages/block-library/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ import * as spacer from './spacer';
import * as table from './table';
import * as tableOfContents from './table-of-contents';
import * as tagCloud from './tag-cloud';
import * as templateContent from './template-content';
import * as templatePart from './template-part';
import * as termDescription from './term-description';
import * as textColumns from './text-columns';
Expand Down Expand Up @@ -160,6 +161,7 @@ const getAllBlocks = () => {
pageList,
pageListItem,
pattern,
templateContent,
preformatted,
pullquote,
reusableBlock,
Expand Down
25 changes: 25 additions & 0 deletions packages/block-library/src/template-content/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"__experimental": true,
"name": "core/template-content",
"title": "Template Content",
"category": "widgets",
"description": "Use template content to replace the template placeholder.",
"textdomain": "default",
"attributes": {
"content": {
"type": "string",
"source": "html"
},
"name": {
"type": "string"
}
},
"supports": {
"customClassName": false,
"className": false,
"html": false,
"inserter": false
}
}
35 changes: 35 additions & 0 deletions packages/block-library/src/template-content/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import {
RichText,
useBlockProps,
__experimentalTemplateContent as TemplateContent,
} from '@wordpress/block-editor';

function TemplateContentEdit( {
attributes: { content, name },
setAttributes,
} ) {
const blockProps = useBlockProps();

return (
<>
<RichText
{ ...blockProps }
identifier="content"
tagName={ 'div' }
value={ content }
onChange={ ( newContent ) =>
setAttributes( { content: newContent } )
}
aria-label={ __( 'Template content' ) }
placeholder={ __( 'Template content' ) }
/>
<TemplateContent name={ name }>Foo!</TemplateContent>
</>
);
}

export default TemplateContentEdit;
18 changes: 18 additions & 0 deletions packages/block-library/src/template-content/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Internal dependencies
*/
import initBlock from '../utils/init-block';
import edit from './edit';
import metadata from './block.json';
import save from './save';

const { name } = metadata;

export { metadata, name };

export const settings = {
edit,
save,
};

export const init = () => initBlock( { name, metadata, settings } );
6 changes: 6 additions & 0 deletions packages/block-library/src/template-content/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import { init } from './';

export default init();
8 changes: 8 additions & 0 deletions packages/block-library/src/template-content/save.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* WordPress dependencies
*/
import { RawHTML } from '@wordpress/element';

export default function templateContentSave( { attributes: { content } } ) {
return <RawHTML>{ content }</RawHTML>;
}
12 changes: 10 additions & 2 deletions packages/blocks/src/api/validation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,10 +573,18 @@ export function getNextNonWhitespaceToken( tokens ) {
* @return {Object[]|null} Array of valid tokenized HTML elements, or null on error
*/
function getHTMLTokens( html, logger = createLogger() ) {
let temp = html;
try {
return new Tokenizer( new DecodeEntityParser() ).tokenize( html );
// Quick way to ensure that the template slot is correctly validated as HTML comment.
if ( temp.includes( '</$wp:template-content' ) ) {
temp = temp.replace(
/<\/(\$wp\:template-content[^>]*)>/gi,
'<!-- $1 -->'
);
}
return new Tokenizer( new DecodeEntityParser() ).tokenize( temp );
} catch ( e ) {
logger.warning( 'Malformed HTML detected: %s', html );
logger.warning( 'Malformed HTML detected: %s', temp );
}

return null;
Expand Down
Loading