From fa438ca92e3a2fe212b6f573cb41937f574199a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Zi=C3=B3=C5=82kowski?= Date: Tue, 3 Dec 2024 11:28:05 +0100 Subject: [PATCH 01/37] =?UTF-8?q?Revert=20"Extensibility:=20Make=20Block?= =?UTF-8?q?=20Bindings=20work=20with=20`editor.BlockEdit`=20hook=20?= =?UTF-8?q?=E2=80=A6"=20(#67516)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit c517e410017f4d65a7c6f03a31c5c2fa15cbbd65. Co-authored-by: gziolo Co-authored-by: SantosGuillamot --- .../src/components/block-edit/edit.js | 12 +- .../block-list/use-block-props/index.js | 2 +- .../src/components/rich-text/index.js | 2 +- .../block-editor/src/hooks/block-bindings.js | 10 +- packages/block-editor/src/hooks/index.js | 1 + .../use-bindings-attributes.js} | 103 ++++++++++++++---- .../block-editor/src/utils/block-bindings.js | 37 ------- packages/e2e-tests/plugins/block-bindings.php | 6 +- .../e2e-tests/plugins/block-bindings/index.js | 45 -------- .../various/block-bindings/post-meta.spec.js | 41 ------- 10 files changed, 94 insertions(+), 165 deletions(-) rename packages/block-editor/src/{components/block-edit/with-block-bindings-support.js => hooks/use-bindings-attributes.js} (73%) diff --git a/packages/block-editor/src/components/block-edit/edit.js b/packages/block-editor/src/components/block-edit/edit.js index 6b1ddd86f4c76e..83d0e3f406f829 100644 --- a/packages/block-editor/src/components/block-edit/edit.js +++ b/packages/block-editor/src/components/block-edit/edit.js @@ -18,8 +18,6 @@ import { useContext, useMemo } from '@wordpress/element'; * Internal dependencies */ import BlockContext from '../block-context'; -import { withBlockBindingsSupport } from './with-block-bindings-support'; -import { canBindBlock } from '../../utils/block-bindings'; /** * Default value used for blocks which do not define their own context needs, @@ -49,8 +47,6 @@ const Edit = ( props ) => { const EditWithFilters = withFilters( 'editor.BlockEdit' )( Edit ); -const EditWithFiltersAndBindings = withBlockBindingsSupport( EditWithFilters ); - const EditWithGeneratedProps = ( props ) => { const { attributes = {}, name } = props; const blockType = getBlockType( name ); @@ -71,12 +67,8 @@ const EditWithGeneratedProps = ( props ) => { return null; } - const EditComponent = canBindBlock( name ) - ? EditWithFiltersAndBindings - : EditWithFilters; - if ( blockType.apiVersion > 1 ) { - return ; + return ; } // Generate a class name for the block's editable form. @@ -90,7 +82,7 @@ const EditWithGeneratedProps = ( props ) => { ); return ( - ( props ) => { const registry = useRegistry(); const blockContext = useContext( BlockContext ); @@ -72,9 +108,9 @@ export const withBlockBindingsSupport = createHigherOrderComponent( () => replacePatternOverrideDefaultBindings( name, - props.attributes?.metadata?.bindings + props.attributes.metadata?.bindings ), - [ props.attributes?.metadata?.bindings, name ] + [ props.attributes.metadata?.bindings, name ] ); // While this hook doesn't directly call any selectors, `useSelect` is @@ -160,7 +196,7 @@ export const withBlockBindingsSupport = createHigherOrderComponent( const hasParentPattern = !! updatedContext[ 'pattern/overrides' ]; const hasPatternOverridesDefaultBinding = - props.attributes?.metadata?.bindings?.[ DEFAULT_ATTRIBUTE ] + props.attributes.metadata?.bindings?.[ DEFAULT_ATTRIBUTE ] ?.source === 'core/pattern-overrides'; const _setAttributes = useCallback( @@ -247,13 +283,40 @@ export const withBlockBindingsSupport = createHigherOrderComponent( ); return ( - + <> + + ); }, 'withBlockBindingSupport' ); + +/** + * Filters a registered block's settings to enhance a block's `edit` component + * to upgrade bound attributes. + * + * @param {WPBlockSettings} settings - Registered block settings. + * @param {string} name - Block name. + * @return {WPBlockSettings} Filtered block settings. + */ +function shimAttributeSource( settings, name ) { + if ( ! canBindBlock( name ) ) { + return settings; + } + + return { + ...settings, + edit: withBlockBindingSupport( settings.edit ), + }; +} + +addFilter( + 'blocks.registerBlockType', + 'core/editor/custom-sources-backwards-compatibility/shim-attribute-source', + shimAttributeSource +); diff --git a/packages/block-editor/src/utils/block-bindings.js b/packages/block-editor/src/utils/block-bindings.js index 82f0dff53531a4..dcf80d985473b2 100644 --- a/packages/block-editor/src/utils/block-bindings.js +++ b/packages/block-editor/src/utils/block-bindings.js @@ -13,43 +13,6 @@ function isObjectEmpty( object ) { return ! object || Object.keys( object ).length === 0; } -export const BLOCK_BINDINGS_ALLOWED_BLOCKS = { - 'core/paragraph': [ 'content' ], - 'core/heading': [ 'content' ], - 'core/image': [ 'id', 'url', 'title', 'alt' ], - 'core/button': [ 'url', 'text', 'linkTarget', 'rel' ], -}; - -/** - * Based on the given block name, - * check if it is possible to bind the block. - * - * @param {string} blockName - The block name. - * @return {boolean} Whether it is possible to bind the block to sources. - */ -export function canBindBlock( blockName ) { - return blockName in BLOCK_BINDINGS_ALLOWED_BLOCKS; -} - -/** - * Based on the given block name and attribute name, - * check if it is possible to bind the block attribute. - * - * @param {string} blockName - The block name. - * @param {string} attributeName - The attribute name. - * @return {boolean} Whether it is possible to bind the block attribute. - */ -export function canBindAttribute( blockName, attributeName ) { - return ( - canBindBlock( blockName ) && - BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ].includes( attributeName ) - ); -} - -export function getBindableAttributes( blockName ) { - return BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ]; -} - /** * Contains utils to update the block `bindings` metadata. * diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php index 1fd6d8468c77db..b86673c2c523d0 100644 --- a/packages/e2e-tests/plugins/block-bindings.php +++ b/packages/e2e-tests/plugins/block-bindings.php @@ -41,11 +41,7 @@ function gutenberg_test_block_bindings_registration() { plugins_url( 'block-bindings/index.js', __FILE__ ), array( 'wp-blocks', - 'wp-block-editor', - 'wp-components', - 'wp-compose', - 'wp-element', - 'wp-hooks', + 'wp-private-apis', ), filemtime( plugin_dir_path( __FILE__ ) . 'block-bindings/index.js' ), true diff --git a/packages/e2e-tests/plugins/block-bindings/index.js b/packages/e2e-tests/plugins/block-bindings/index.js index 63c463e197fa8a..5c364257caed19 100644 --- a/packages/e2e-tests/plugins/block-bindings/index.js +++ b/packages/e2e-tests/plugins/block-bindings/index.js @@ -1,9 +1,4 @@ const { registerBlockBindingsSource } = wp.blocks; -const { InspectorControls } = wp.blockEditor; -const { PanelBody, TextControl } = wp.components; -const { createHigherOrderComponent } = wp.compose; -const { createElement: el, Fragment } = wp.element; -const { addFilter } = wp.hooks; const { fieldsList } = window.testingBindings || {}; const getValues = ( { bindings } ) => { @@ -51,43 +46,3 @@ registerBlockBindingsSource( { getValues, canUserEditValue: () => true, } ); - -const withBlockBindingsInspectorControl = createHigherOrderComponent( - ( BlockEdit ) => { - return ( props ) => { - if ( ! props.attributes?.metadata?.bindings?.content ) { - return el( BlockEdit, props ); - } - - return el( - Fragment, - {}, - el( BlockEdit, props ), - el( - InspectorControls, - {}, - el( - PanelBody, - { title: 'Bindings' }, - el( TextControl, { - __next40pxDefaultSize: true, - __nextHasNoMarginBottom: true, - label: 'Content', - value: props.attributes.content, - onChange: ( content ) => - props.setAttributes( { - content, - } ), - } ) - ) - ) - ); - }; - } -); - -addFilter( - 'editor.BlockEdit', - 'testing/bindings-inspector-control', - withBlockBindingsInspectorControl -); diff --git a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js index 318707e22f098d..32334bfc777f2a 100644 --- a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js +++ b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js @@ -524,47 +524,6 @@ test.describe( 'Post Meta source', () => { previewPage.locator( '#connected-paragraph' ) ).toHaveText( 'new value' ); } ); - - test( 'should be possible to edit the value of the connected custom fields in the inspector control registered by the plugin', async ( { - editor, - page, - } ) => { - await editor.insertBlock( { - name: 'core/paragraph', - attributes: { - anchor: 'connected-paragraph', - content: 'fallback content', - metadata: { - bindings: { - content: { - source: 'core/post-meta', - args: { - key: 'movie_field', - }, - }, - }, - }, - }, - } ); - const contentInput = page.getByRole( 'textbox', { - name: 'Content', - } ); - await expect( contentInput ).toHaveValue( - 'Movie field default value' - ); - await contentInput.fill( 'new value' ); - // Check that the paragraph content attribute didn't change. - const [ paragraphBlockObject ] = await editor.getBlocks(); - expect( paragraphBlockObject.attributes.content ).toBe( - 'fallback content' - ); - // Check the value of the custom field is being updated by visiting the frontend. - const previewPage = await editor.openPreviewPage(); - await expect( - previewPage.locator( '#connected-paragraph' ) - ).toHaveText( 'new value' ); - } ); - test( 'should be possible to connect movie fields through the attributes panel', async ( { editor, page, From fcee058e3bb787ee041c170958e15e66a6302352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Greg=20Zi=C3=B3=C5=82kowski?= Date: Tue, 3 Dec 2024 11:44:23 +0100 Subject: [PATCH 02/37] Scripts: Make React Fast Refresh work with multiple blocks (#64924) * Scripts: Make React Fast Refresh Work with multiple blocks * Properly pass the unique name for the build and updates test snapshots * Apply suggestions from code review * Add changelog entry Co-authored-by: gziolo Co-authored-by: sirreal --- .../lib/index.js | 10 +++++++ .../test/__snapshots__/build.js.snap | 12 ++++---- packages/scripts/CHANGELOG.md | 4 +++ packages/scripts/config/webpack.config.js | 29 ++++++++++++++++++- 4 files changed, 48 insertions(+), 7 deletions(-) diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index cf780d7370dcfc..8bc7cb29312161 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -387,6 +387,16 @@ class DependencyExtractionWebpackPlugin { assetData.type = 'module'; } + if ( compilation.options?.optimization?.runtimeChunk !== false ) { + // Sets the script handle for the shared runtime file so WordPress registers it only once when using the asset file. + assetData.handle = + compilation.name + + '-' + + chunkJSFile + .replace( /\\/g, '/' ) + .replace( jsExtensionRegExp, '' ); + } + if ( combineAssets ) { combinedAssetsData[ chunkJSFile ] = assetData; continue; diff --git a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap index 8ea40b00d7c2d1..bafae8eb914867 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -265,17 +265,17 @@ exports[`DependencyExtractionWebpackPlugin modules Webpack \`polyfill-magic-comm exports[`DependencyExtractionWebpackPlugin modules Webpack \`polyfill-magic-comment-minified\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'a.asset.php' should match snapshot 1`] = ` -" array('@wordpress/blob'), 'version' => 'ee5ac21a1f0003d732e6', 'type' => 'module'); +" array('@wordpress/blob'), 'version' => 'ee5ac21a1f0003d732e6', 'type' => 'module', 'handle' => 'runtime-chunk-single-modules-a'); " `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'b.asset.php' should match snapshot 1`] = ` -" array('@wordpress/blob', 'lodash'), 'version' => '5b112b32c6db548c2997', 'type' => 'module'); +" array('@wordpress/blob', 'lodash'), 'version' => '5b112b32c6db548c2997', 'type' => 'module', 'handle' => 'runtime-chunk-single-modules-b'); " `; exports[`DependencyExtractionWebpackPlugin modules Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'runtime.asset.php' should match snapshot 1`] = ` -" array(), 'version' => 'b1ca4106075e0bd94f9c', 'type' => 'module'); +" array(), 'version' => 'b1ca4106075e0bd94f9c', 'type' => 'module', 'handle' => 'runtime-chunk-single-modules-runtime'); " `; @@ -681,17 +681,17 @@ exports[`DependencyExtractionWebpackPlugin scripts Webpack \`polyfill-magic-comm exports[`DependencyExtractionWebpackPlugin scripts Webpack \`polyfill-magic-comment-minified\` should produce expected output: External modules should match snapshot 1`] = `[]`; exports[`DependencyExtractionWebpackPlugin scripts Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'a.asset.php' should match snapshot 1`] = ` -" array('wp-blob'), 'version' => 'd091f1cbbf7603d6e12c'); +" array('wp-blob'), 'version' => 'd091f1cbbf7603d6e12c', 'handle' => 'runtime-chunk-single-scripts-a'); " `; exports[`DependencyExtractionWebpackPlugin scripts Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'b.asset.php' should match snapshot 1`] = ` -" array('lodash', 'wp-blob'), 'version' => '845bc6d4ffbdb9419ebd'); +" array('lodash', 'wp-blob'), 'version' => '845bc6d4ffbdb9419ebd', 'handle' => 'runtime-chunk-single-scripts-b'); " `; exports[`DependencyExtractionWebpackPlugin scripts Webpack \`runtime-chunk-single\` should produce expected output: Asset file 'runtime.asset.php' should match snapshot 1`] = ` -" array(), 'version' => '717eb779e609d175a7dd'); +" array(), 'version' => '717eb779e609d175a7dd', 'handle' => 'runtime-chunk-single-scripts-runtime'); " `; diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index 55143ae792ae5b..ceaa25c4ff9a09 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Bug Fix + +- Make React Fast Refresh in the `start` command work with multiple blocks ([64924](https://github.com/WordPress/gutenberg/pull/64924)). + ## 30.6.0 (2024-11-27) ## 30.5.1 (2024-11-18) diff --git a/packages/scripts/config/webpack.config.js b/packages/scripts/config/webpack.config.js index f9ef7dc5b7acca..2c3d423fcbd1e8 100644 --- a/packages/scripts/config/webpack.config.js +++ b/packages/scripts/config/webpack.config.js @@ -7,7 +7,7 @@ const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); const webpack = require( 'webpack' ); const browserslist = require( 'browserslist' ); const MiniCSSExtractPlugin = require( 'mini-css-extract-plugin' ); -const { basename, dirname, resolve } = require( 'path' ); +const { basename, dirname, relative, resolve, sep } = require( 'path' ); const ReactRefreshWebpackPlugin = require( '@pmmmwh/react-refresh-webpack-plugin' ); const RtlCssPlugin = require( 'rtlcss-webpack-plugin' ); const TerserPlugin = require( 'terser-webpack-plugin' ); @@ -115,6 +115,7 @@ const baseConfig = { optimization: { // Only concatenate modules in production, when not analyzing bundles. concatenateModules: isProduction && ! process.env.WP_BUNDLE_ANALYZER, + runtimeChunk: hasReactFastRefresh && 'single', splitChunks: { cacheGroups: { style: { @@ -340,6 +341,32 @@ const scriptConfig = { } } ); + if ( hasReactFastRefresh ) { + // Prepends the file reference to the shared runtime chunk to every script type defined for the block. + const runtimePath = relative( + dirname( absoluteFrom ), + fromProjectRoot( + getWordPressSrcDirectory() + + sep + + 'runtime.js' + ) + ); + const fields = + getBlockJsonScriptFields( blockJson ); + for ( const [ fieldName ] of Object.entries( + fields + ) ) { + blockJson[ fieldName ] = [ + `file:${ runtimePath }`, + ...( Array.isArray( + blockJson[ fieldName ] + ) + ? blockJson[ fieldName ] + : [ blockJson[ fieldName ] ] ), + ]; + } + } + return JSON.stringify( blockJson, null, 2 ); } From 5c76815eb6e47f8cdd8f32c919f431467b078366 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Tue, 3 Dec 2024 12:14:33 +0100 Subject: [PATCH 03/37] Quick Edit: add Template field (#66591) Co-authored-by: louwie17 Co-authored-by: gigitux Co-authored-by: youknowriad --- package-lock.json | 1 + packages/base-styles/_z-index.scss | 1 + .../src/components/post-edit/index.js | 36 ++- .../src/dataviews/store/private-actions.ts | 2 + packages/fields/README.md | 4 + packages/fields/package.json | 1 + packages/fields/src/actions/utils.ts | 4 +- packages/fields/src/fields/index.ts | 1 + packages/fields/src/fields/template/index.ts | 22 ++ .../fields/src/fields/template/style.scss | 23 ++ .../src/fields/template/template-edit.tsx | 210 ++++++++++++++++++ packages/fields/src/style.scss | 1 + packages/fields/tsconfig.json | 1 + 13 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 packages/fields/src/fields/template/index.ts create mode 100644 packages/fields/src/fields/template/style.scss create mode 100644 packages/fields/src/fields/template/template-edit.tsx diff --git a/package-lock.json b/package-lock.json index 98865c9d041a58..834bb344016d26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54694,6 +54694,7 @@ "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", "@wordpress/blob": "*", + "@wordpress/block-editor": "*", "@wordpress/blocks": "*", "@wordpress/components": "*", "@wordpress/compose": "*", diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index c2ee8f698c2c80..af679edb910642 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -132,6 +132,7 @@ $z-layers: ( ".editor-action-modal": 1000001, ".editor-post-template__swap-template-modal": 1000001, ".edit-site-template-panel__replace-template-modal": 1000001, + ".fields-controls__template-modal": 1000001, // Note: The ConfirmDialog component's z-index is being set to 1000001 in packages/components/src/confirm-dialog/styles.ts // because it uses emotion and not sass. We need it to render on top its parent popover. diff --git a/packages/edit-site/src/components/post-edit/index.js b/packages/edit-site/src/components/post-edit/index.js index 3e75ef71d1ac9b..9a99a987089c1a 100644 --- a/packages/edit-site/src/components/post-edit/index.js +++ b/packages/edit-site/src/components/post-edit/index.js @@ -19,6 +19,8 @@ import { privateApis as editorPrivateApis } from '@wordpress/editor'; */ import Page from '../page'; import { unlock } from '../../lock-unlock'; +import usePatternSettings from '../page-patterns/use-pattern-settings'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; const { PostCardPanel, usePostFields } = unlock( editorPrivateApis ); @@ -85,6 +87,12 @@ function PostEditForm( { postType, postId } ) { 'slug', 'parent', 'comment_status', + { + label: __( 'Template' ), + labelPosition: 'side', + id: 'template', + layout: 'regular', + }, ].filter( ( field ) => ids.length === 1 || @@ -123,6 +131,32 @@ function PostEditForm( { postType, postId } ) { setMultiEdits( {} ); }, [ ids ] ); + const { ExperimentalBlockEditorProvider } = unlock( + blockEditorPrivateApis + ); + const settings = usePatternSettings(); + + /** + * The template field depends on the block editor settings. + * This is a workaround to ensure that the block editor settings are available. + * For more information, see: https://github.com/WordPress/gutenberg/issues/67521 + */ + const fieldsWithDependency = useMemo( () => { + return fields.map( ( field ) => { + if ( field.id === 'template' ) { + return { + ...field, + Edit: ( data ) => ( + + + + ), + }; + } + return field; + } ); + }, [ fields, settings ] ); + return ( { ids.length === 1 && ( @@ -130,7 +164,7 @@ function PostEditForm( { postType, postId } ) { ) } diff --git a/packages/editor/src/dataviews/store/private-actions.ts b/packages/editor/src/dataviews/store/private-actions.ts index e61ade7e830364..6906629fc80027 100644 --- a/packages/editor/src/dataviews/store/private-actions.ts +++ b/packages/editor/src/dataviews/store/private-actions.ts @@ -34,6 +34,7 @@ import { statusField, authorField, titleField, + templateField, } from '@wordpress/fields'; export function registerEntityAction< Item >( @@ -171,6 +172,7 @@ export const registerPostTypeSchema = postTypeConfig.supports?.[ 'page-attributes' ] && parentField, postTypeConfig.supports?.comments && commentStatusField, passwordField, + templateField, ].filter( Boolean ); registry.batch( () => { diff --git a/packages/fields/README.md b/packages/fields/README.md index 6723611d2d9686..2fc512b9432645 100644 --- a/packages/fields/README.md +++ b/packages/fields/README.md @@ -123,6 +123,10 @@ Undocumented declaration. Status field for BasePost. +### templateField + +Undocumented declaration. + ### titleField Undocumented declaration. diff --git a/packages/fields/package.json b/packages/fields/package.json index eb60f448fc13e6..beaaf4f9813013 100644 --- a/packages/fields/package.json +++ b/packages/fields/package.json @@ -35,6 +35,7 @@ "@babel/runtime": "7.25.7", "@wordpress/api-fetch": "*", "@wordpress/blob": "*", + "@wordpress/block-editor": "*", "@wordpress/blocks": "*", "@wordpress/components": "*", "@wordpress/compose": "*", diff --git a/packages/fields/src/actions/utils.ts b/packages/fields/src/actions/utils.ts index efd389405b5be8..7bc08573f0b9f8 100644 --- a/packages/fields/src/actions/utils.ts +++ b/packages/fields/src/actions/utils.ts @@ -22,7 +22,9 @@ export function isTemplateOrTemplatePart( return p.type === 'wp_template' || p.type === 'wp_template_part'; } -export function getItemTitle( item: Post ): string { +export function getItemTitle( item: { + title: string | { rendered: string } | { raw: string }; +} ) { if ( typeof item.title === 'string' ) { return decodeEntities( item.title ); } diff --git a/packages/fields/src/fields/index.ts b/packages/fields/src/fields/index.ts index 5ea4235af1d964..2cdf89ee13fb02 100644 --- a/packages/fields/src/fields/index.ts +++ b/packages/fields/src/fields/index.ts @@ -2,6 +2,7 @@ export { default as slugField } from './slug'; export { default as titleField } from './title'; export { default as orderField } from './order'; export { default as featuredImageField } from './featured-image'; +export { default as templateField } from './template'; export { default as parentField } from './parent'; export { default as passwordField } from './password'; export { default as statusField } from './status'; diff --git a/packages/fields/src/fields/template/index.ts b/packages/fields/src/fields/template/index.ts new file mode 100644 index 00000000000000..7315b4ba349b1c --- /dev/null +++ b/packages/fields/src/fields/template/index.ts @@ -0,0 +1,22 @@ +/** + * WordPress dependencies + */ +import type { Field } from '@wordpress/dataviews'; + +/** + * Internal dependencies + */ +import { __ } from '@wordpress/i18n'; +import type { BasePost } from '../../types'; +import { TemplateEdit } from './template-edit'; + +const templateField: Field< BasePost > = { + id: 'template', + type: 'text', + label: __( 'Template' ), + getValue: ( { item } ) => item.template, + Edit: TemplateEdit, + enableSorting: false, +}; + +export default templateField; diff --git a/packages/fields/src/fields/template/style.scss b/packages/fields/src/fields/template/style.scss new file mode 100644 index 00000000000000..a0c2fafec73893 --- /dev/null +++ b/packages/fields/src/fields/template/style.scss @@ -0,0 +1,23 @@ +.fields-controls__template-modal { + z-index: z-index(".fields-controls__template-modal"); +} + +.fields-controls__template-content .block-editor-block-patterns-list { + column-count: 2; + column-gap: $grid-unit-30; + + // Small top padding required to avoid cutting off the visible outline when hovering items + padding-top: $border-width-focus-fallback; + + @include break-medium() { + column-count: 3; + } + + @include break-wide() { + column-count: 4; + } + + .block-editor-block-patterns-list__list-item { + break-inside: avoid-column; + } +} diff --git a/packages/fields/src/fields/template/template-edit.tsx b/packages/fields/src/fields/template/template-edit.tsx new file mode 100644 index 00000000000000..c17364568a4578 --- /dev/null +++ b/packages/fields/src/fields/template/template-edit.tsx @@ -0,0 +1,210 @@ +/** + * WordPress dependencies + */ +import { useCallback, useMemo, useState } from '@wordpress/element'; +// @ts-ignore +import { parse } from '@wordpress/blocks'; +import type { WpTemplate } from '@wordpress/core-data'; +import { store as coreStore } from '@wordpress/core-data'; +import type { DataFormControlProps } from '@wordpress/dataviews'; + +/** + * Internal dependencies + */ +// @ts-expect-error block-editor is not typed correctly. +import { __experimentalBlockPatternsList as BlockPatternsList } from '@wordpress/block-editor'; +import { + Button, + Dropdown, + MenuGroup, + MenuItem, + Modal, +} from '@wordpress/components'; +import { useAsyncList } from '@wordpress/compose'; +import { useSelect } from '@wordpress/data'; +import { decodeEntities } from '@wordpress/html-entities'; +import { __ } from '@wordpress/i18n'; +import { getItemTitle } from '../../actions/utils'; +import type { BasePost } from '../../types'; +import { unlock } from '../../lock-unlock'; + +export const TemplateEdit = ( { + data, + field, + onChange, +}: DataFormControlProps< BasePost > ) => { + const { id } = field; + const postType = data.type; + const postId = + typeof data.id === 'number' ? data.id : parseInt( data.id, 10 ); + const slug = data.slug; + + const { availableTemplates, templates } = useSelect( + ( select ) => { + const allTemplates = + select( coreStore ).getEntityRecords< WpTemplate >( + 'postType', + 'wp_template', + { + per_page: -1, + post_type: postType, + } + ) ?? []; + + const { getHomePage, getPostsPageId } = unlock( + select( coreStore ) + ); + + const isPostsPage = getPostsPageId() === +postId; + const isFrontPage = + postType === 'page' && getHomePage()?.postId === +postId; + + const allowSwitchingTemplate = ! isPostsPage && ! isFrontPage; + + return { + templates: allTemplates, + availableTemplates: allowSwitchingTemplate + ? allTemplates.filter( + ( template ) => + template.is_custom && + template.slug !== data.template && + !! template.content.raw // Skip empty templates. + ) + : [], + }; + }, + [ data.template, postId, postType ] + ); + + const templatesAsPatterns = useMemo( + () => + availableTemplates.map( ( template ) => ( { + name: template.slug, + blocks: parse( template.content.raw ), + title: decodeEntities( template.title.rendered ), + id: template.id, + } ) ), + [ availableTemplates ] + ); + + const shownTemplates = useAsyncList( templatesAsPatterns ); + + const value = field.getValue( { item: data } ); + + const currentTemplate = useSelect( + ( select ) => { + const foundTemplate = templates?.find( + ( template ) => template.slug === value + ); + + if ( foundTemplate ) { + return foundTemplate; + } + + let slugToCheck; + // In `draft` status we might not have a slug available, so we use the `single` + // post type templates slug(ex page, single-post, single-product etc..). + // Pages do not need the `single` prefix in the slug to be prioritized + // through template hierarchy. + if ( slug ) { + slugToCheck = + postType === 'page' + ? `${ postType }-${ slug }` + : `single-${ postType }-${ slug }`; + } else { + slugToCheck = + postType === 'page' ? 'page' : `single-${ postType }`; + } + + if ( postType ) { + const templateId = select( coreStore ).getDefaultTemplateId( { + slug: slugToCheck, + } ); + + return select( coreStore ).getEntityRecord( + 'postType', + 'wp_template', + templateId + ); + } + }, + [ postType, slug, templates, value ] + ); + + const [ showModal, setShowModal ] = useState( false ); + + const onChangeControl = useCallback( + ( newValue: string ) => + onChange( { + [ id ]: newValue, + } ), + [ id, onChange ] + ); + + return ( +
+ ( + + ) } + renderContent={ ( { onToggle } ) => ( + + { + setShowModal( true ); + onToggle(); + } } + > + { __( 'Swap template' ) } + + { + // The default template in a post is indicated by an empty string + value !== '' && ( + { + onChangeControl( '' ); + onToggle(); + } } + > + { __( 'Use default template' ) } + + ) + } + + ) } + /> + { showModal && ( + setShowModal( false ) } + overlayClassName="fields-controls__template-modal" + isFullScreen + > +
+ { + onChangeControl( template.name ); + setShowModal( false ); + } } + /> +
+
+ ) } +
+ ); +}; diff --git a/packages/fields/src/style.scss b/packages/fields/src/style.scss index 05cf5652248777..582044235aef1a 100644 --- a/packages/fields/src/style.scss +++ b/packages/fields/src/style.scss @@ -1,3 +1,4 @@ @import "./components/create-template-part-modal/style.scss"; @import "./fields/slug/style.scss"; @import "./fields/featured-image/style.scss"; +@import "./fields/template/style.scss"; diff --git a/packages/fields/tsconfig.json b/packages/fields/tsconfig.json index 531afb5bb2d873..46ac86d48e11ee 100644 --- a/packages/fields/tsconfig.json +++ b/packages/fields/tsconfig.json @@ -27,6 +27,7 @@ { "path": "../private-apis" }, { "path": "../router" }, { "path": "../url" }, + { "path": "../block-editor" }, { "path": "../warning" } ], "include": [ "src" ] From 4d225cc2ba6f09822227e7a820b8a555be7c4d48 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:22:28 +0100 Subject: [PATCH 04/37] [mini] Preload: add post type (#67518) Co-authored-by: ellatrix Co-authored-by: Mamaduka --- backport-changelog/6.8/7695.md | 1 + lib/compat/wordpress-6.8/preload.php | 1 + 2 files changed, 2 insertions(+) diff --git a/backport-changelog/6.8/7695.md b/backport-changelog/6.8/7695.md index d69a59f2d67d12..08b780e2afb0d7 100644 --- a/backport-changelog/6.8/7695.md +++ b/backport-changelog/6.8/7695.md @@ -4,3 +4,4 @@ https://github.com/WordPress/wordpress-develop/pull/7695 * https://github.com/WordPress/gutenberg/pull/67465 * https://github.com/WordPress/gutenberg/pull/66579 * https://github.com/WordPress/gutenberg/pull/66654 +* https://github.com/WordPress/gutenberg/pull/67518 diff --git a/lib/compat/wordpress-6.8/preload.php b/lib/compat/wordpress-6.8/preload.php index 0a36ea7f7227d4..0e887fc081bcb5 100644 --- a/lib/compat/wordpress-6.8/preload.php +++ b/lib/compat/wordpress-6.8/preload.php @@ -22,6 +22,7 @@ function gutenberg_block_editor_preload_paths_6_8( $paths, $context ) { $route_for_post = rest_get_route_for_post( $post ); if ( $route_for_post ) { $paths[] = add_query_arg( 'context', 'edit', $route_for_post ); + $paths[] = add_query_arg( 'context', 'edit', '/wp/v2/types/' . $post->post_type ); if ( 'page' === $post->post_type ) { $paths[] = add_query_arg( 'slug', From 0ff919f2c6a94e657987a8440c75b5e4f2db0b13 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Tue, 3 Dec 2024 12:49:06 +0000 Subject: [PATCH 05/37] Docs: Remove invalid key projects links on the documentation. (#67491) Co-authored-by: jorgefilipecosta --- docs/contributors/repository-management.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/contributors/repository-management.md b/docs/contributors/repository-management.md index e57f762a605394..5bb971bfaf2efc 100644 --- a/docs/contributors/repository-management.md +++ b/docs/contributors/repository-management.md @@ -165,9 +165,3 @@ If you meet this criterion of several meaningful contributions having been accep ## Projects We use [GitHub projects](https://github.com/WordPress/gutenberg/projects) to keep track of details that aren't immediately actionable, but that we want to keep around for future reference. - -Some key projects include: - -- [Phase 2](https://github.com/WordPress/gutenberg/projects/13) - Development tasks needed for Phase 2 of Gutenberg. -- [Phase 2 design](https://github.com/WordPress/gutenberg/projects/21) - Tasks for design in Phase 2. Note: specific projects may have their own boards. -- [Ideas](https://github.com/WordPress/gutenberg/projects/8) - Project containing tickets that, while closed for the time being, can be revisited in the future. From 1c3cea43b0fca853f351e4bc08ba840df7de2469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Gomes?= Date: Tue, 3 Dec 2024 14:24:57 +0000 Subject: [PATCH 06/37] Exclude Set instance methods from polyfills (#67230) * Exclude Set instance methods from polyfills * Switch to regexp exclusions --- .../polyfill-exclusions.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/babel-preset-default/polyfill-exclusions.js b/packages/babel-preset-default/polyfill-exclusions.js index 507396c930b99c..ca8c045d124146 100644 --- a/packages/babel-preset-default/polyfill-exclusions.js +++ b/packages/babel-preset-default/polyfill-exclusions.js @@ -7,4 +7,25 @@ module.exports = [ // This is an IE-only feature which we don't use, and don't want to polyfill. // @see https://github.com/WordPress/gutenberg/pull/49234 'web.immediate', + // Remove Set feature polyfills. + // + // The Babel/core-js integration has a severe limitation, in that any Set + // objects (e.g. `new Set()`) are assumed to need all instance methods, and + // get them all polyfilled. There is no validation as to whether those + // methods are actually in use. + // + // This limitation causes a number of packages to unnecessarily get a + // dependency on `wp-polyfill`, which in most cases gets loaded as part of + // the critical path and can thus have an impact on performance. + // + // There is no good solution to this, and the one we've opted for here is + // to disable polyfilling these features entirely. Developers will need to + // take care not to use them in scenarios where the code may be running in + // older browsers without native support for them. + // + // These need to be specified as both `es.` and `esnext.` due to the way + // internal dependencies are set up in Babel / core-js. + // + // @see https://github.com/WordPress/gutenberg/pull/67230 + /^es(next)?\.set\./, ]; From 7631986644c82b2c8ff7481e95a64124644f7c1d Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Tue, 3 Dec 2024 08:24:33 -0800 Subject: [PATCH 07/37] Split view with meta boxes even with legacy canvas (#66706) * Split view with meta boxes with non-iframed canvas * Fix scrolling of device previews * Consolidate styles and add comments * Do the same thing without adding a prop to BlockCanvas * Fix horizontal overflow of device previews Co-authored-by: stokesman Co-authored-by: t-hamano Co-authored-by: cbravobernal Co-authored-by: jartes Co-authored-by: bph Co-authored-by: ndiego Co-authored-by: MadtownLems --- .../src/components/block-canvas/index.js | 8 ++--- .../src/components/use-resize-canvas/index.js | 2 +- .../edit-post/src/components/layout/index.js | 23 +++--------- .../src/components/layout/style.scss | 8 ++--- .../components/editor-interface/style.scss | 3 +- .../src/components/visual-editor/index.js | 1 + .../src/components/visual-editor/style.scss | 35 +++++++++++++++---- 7 files changed, 44 insertions(+), 36 deletions(-) diff --git a/packages/block-editor/src/components/block-canvas/index.js b/packages/block-editor/src/components/block-canvas/index.js index c399f38054ed4d..36aca7fa1c7220 100644 --- a/packages/block-editor/src/components/block-canvas/index.js +++ b/packages/block-editor/src/components/block-canvas/index.js @@ -56,7 +56,8 @@ export function ExperimentalBlockCanvas( { return ( { children } @@ -81,6 +78,7 @@ export function ExperimentalBlockCanvas( { return (