From 252418b734f0204ede36aeafebb90247523bb071 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Mon, 29 Jan 2024 12:47:38 +0100 Subject: [PATCH 01/45] Interactivity: Fix broken react usage in published package (#58258) @wordpress/interactivity depends on preact, not react. - Enable the "no react in scope" eslint rule for the interactivity packages. - Add pragmas to set the JSX transform the use createElement. - Import Preact's `h` as `createElement` explicitly. This setup ensures that `@wordpress/babel-plugin-import-jsx-pragma` does not add a `createElement` import from React because it detects createElement is already in scope. --- .eslintrc.js | 6 ++++++ packages/interactivity/CHANGELOG.md | 4 ++++ packages/interactivity/src/directives.js | 3 +++ packages/interactivity/src/hooks.tsx | 13 ++++++++++--- tools/webpack/interactivity.js | 12 +++--------- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 18f8d279c9946f..01f8a506addc7a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -406,5 +406,11 @@ module.exports = { ], }, }, + { + files: [ 'packages/interactivity*/src/**' ], + rules: { + 'react/react-in-jsx-scope': 'error', + }, + }, ], }; diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md index 49b6b28b91a96f..666e358cde60d4 100644 --- a/packages/interactivity/CHANGELOG.md +++ b/packages/interactivity/CHANGELOG.md @@ -6,6 +6,10 @@ - Break up init with yielding to main to prevent long task from hydration. ([#58227](https://github.com/WordPress/gutenberg/pull/58227)) +### Bug Fixes + +- Ensure Preact is used in published packages ([58258](https://github.com/WordPress/gutenberg/pull/58258). + ## 4.0.0 (2024-01-24) ### Enhancements diff --git a/packages/interactivity/src/directives.js b/packages/interactivity/src/directives.js index 250d3bde6084c9..38849f53d7f953 100644 --- a/packages/interactivity/src/directives.js +++ b/packages/interactivity/src/directives.js @@ -1,6 +1,9 @@ +/* @jsx createElement */ + /** * External dependencies */ +import { h as createElement } from 'preact'; import { useContext, useMemo, useRef } from 'preact/hooks'; import { deepSignal, peek } from 'deepsignal'; diff --git a/packages/interactivity/src/hooks.tsx b/packages/interactivity/src/hooks.tsx index c133eb9981880b..383bcaa1a4ae83 100644 --- a/packages/interactivity/src/hooks.tsx +++ b/packages/interactivity/src/hooks.tsx @@ -1,7 +1,14 @@ +/* @jsx createElement */ + /** * External dependencies */ -import { h, options, createContext, cloneElement } from 'preact'; +import { + h as createElement, + options, + createContext, + cloneElement, +} from 'preact'; import { useRef, useCallback, useContext } from 'preact/hooks'; import type { VNode, Context, RefObject } from 'preact'; @@ -59,7 +66,7 @@ interface Scope { evaluate: Evaluate; context: Context< any >; ref: RefObject< HTMLElement >; - attributes: h.JSX.HTMLAttributes; + attributes: createElement.JSX.HTMLAttributes; } interface Evaluate { @@ -372,7 +379,7 @@ options.vnode = ( vnode: VNode< any > ) => { priorityLevels, originalProps: props, type: vnode.type, - element: h( vnode.type as any, props ), + element: createElement( vnode.type as any, props ), top: true, }; vnode.type = Directives; diff --git a/tools/webpack/interactivity.js b/tools/webpack/interactivity.js index 5dd9192c855661..2aadcbf79a158e 100644 --- a/tools/webpack/interactivity.js +++ b/tools/webpack/interactivity.js @@ -17,8 +17,8 @@ module.exports = { ...baseConfig, name: 'interactivity', entry: { - index: `./packages/interactivity/src/index.js`, - router: `./packages/interactivity-router/src/index.js`, + index: './packages/interactivity', + router: './packages/interactivity-router', navigation: './packages/block-library/src/navigation/view.js', query: './packages/block-library/src/query/view.js', image: './packages/block-library/src/image/view.js', @@ -57,13 +57,7 @@ module.exports = { configFile: false, presets: [ '@babel/preset-typescript', - [ - '@babel/preset-react', - { - runtime: 'automatic', - importSource: 'preact', - }, - ], + '@babel/preset-react', ], }, }, From e3f4f76fc8b1f4eeecc641b30f3d16b9a19eb552 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:10:21 +0200 Subject: [PATCH 02/45] Block editor: selectors: avoid has() or double get() on Maps (#58372) --- packages/block-editor/src/store/selectors.js | 26 +++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 099c6b30222efc..0f6f7c33a91fa0 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -157,12 +157,13 @@ export function getBlock( state, clientId ) { export const __unstableGetBlockWithoutInnerBlocks = createSelector( ( state, clientId ) => { - if ( ! state.blocks.byClientId.has( clientId ) ) { + const block = state.blocks.byClientId.get( clientId ); + if ( ! block ) { return null; } return { - ...state.blocks.byClientId.get( clientId ), + ...block, attributes: getBlockAttributes( state, clientId ), }; }, @@ -540,9 +541,7 @@ export function getSelectedBlock( state ) { * @return {?string} Root client ID, if exists */ export function getBlockRootClientId( state, clientId ) { - return state.blocks.parents.has( clientId ) - ? state.blocks.parents.get( clientId ) - : null; + return state.blocks.parents.get( clientId ) ?? null; } /** @@ -558,8 +557,7 @@ export const getBlockParents = createSelector( ( state, clientId, ascending = false ) => { const parents = []; let current = clientId; - while ( !! state.blocks.parents.get( current ) ) { - current = state.blocks.parents.get( current ); + while ( ( current = state.blocks.parents.get( current ) ) ) { parents.push( current ); } @@ -2737,13 +2735,10 @@ export const __unstableGetContentLockingParent = createSelector( ( state, clientId ) => { let current = clientId; let result; - while ( state.blocks.parents.has( current ) ) { - current = state.blocks.parents.get( current ); + while ( ( current = state.blocks.parents.get( current ) ) ) { if ( - ( current && - getBlockName( state, current ) === 'core/block' ) || - ( current && - getTemplateLock( state, current ) === 'contentOnly' ) + getBlockName( state, current ) === 'core/block' || + getTemplateLock( state, current ) === 'contentOnly' ) { result = current; } @@ -2869,8 +2864,9 @@ export function __unstableIsWithinBlockOverlay( state, clientId ) { export const getBlockEditingMode = createRegistrySelector( ( select ) => ( state, clientId = '' ) => { - if ( state.blockEditingModes.has( clientId ) ) { - return state.blockEditingModes.get( clientId ); + const blockEditingMode = state.blockEditingModes.get( clientId ); + if ( blockEditingMode ) { + return blockEditingMode; } if ( ! clientId ) { return 'default'; From cca1a209bcaf5456a99c53abd34408636b2742ea Mon Sep 17 00:00:00 2001 From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:25:13 +0100 Subject: [PATCH 03/45] Block Bindings: Simplify block bindings object (#58337) * Use namespaces in bindings sources * Change `value` for `key` in post meta source * Change `attributes` name for `args` * Add safety check in post meta source * Remove `source` level * Change comment * Adapt pattern overrides without `source`level --- .../block-bindings/sources/pattern.php | 2 +- .../block-bindings/sources/post-meta.php | 8 ++++++-- lib/compat/wordpress-6.5/blocks.php | 20 ++++++++----------- .../src/components/rich-text/index.js | 3 +-- .../src/hooks/use-bindings-attributes.js | 7 ++----- packages/block-library/src/block/edit.js | 4 ++-- packages/block-library/src/button/edit.js | 5 ++--- packages/block-library/src/image/edit.js | 5 ++--- packages/block-library/src/image/image.js | 6 +++--- packages/editor/src/bindings/post-meta.js | 4 ++-- .../components/partial-syncing-controls.js | 14 ++++++------- .../editor/various/pattern-overrides.spec.js | 6 +++--- 12 files changed, 38 insertions(+), 46 deletions(-) diff --git a/lib/compat/wordpress-6.5/block-bindings/sources/pattern.php b/lib/compat/wordpress-6.5/block-bindings/sources/pattern.php index 65ddb7278e7035..9540e329596c99 100644 --- a/lib/compat/wordpress-6.5/block-bindings/sources/pattern.php +++ b/lib/compat/wordpress-6.5/block-bindings/sources/pattern.php @@ -28,7 +28,7 @@ } }; wp_block_bindings_register_source( - 'pattern_attributes', + 'core/pattern-attributes', array( 'label' => __( 'Pattern Attributes' ), 'apply' => $pattern_source_callback, diff --git a/lib/compat/wordpress-6.5/block-bindings/sources/post-meta.php b/lib/compat/wordpress-6.5/block-bindings/sources/post-meta.php index e52b4f289ccdd3..6d8951b5660ab1 100644 --- a/lib/compat/wordpress-6.5/block-bindings/sources/post-meta.php +++ b/lib/compat/wordpress-6.5/block-bindings/sources/post-meta.php @@ -6,6 +6,10 @@ */ if ( function_exists( 'wp_block_bindings_register_source' ) ) { $post_meta_source_callback = function ( $source_attrs ) { + if ( ! isset( $source_attrs['key'] ) ) { + return null; + } + // Use the postId attribute if available if ( isset( $source_attrs['postId'] ) ) { $post_id = $source_attrs['postId']; @@ -14,10 +18,10 @@ $post_id = get_the_ID(); } - return get_post_meta( $post_id, $source_attrs['value'], true ); + return get_post_meta( $post_id, $source_attrs['key'], true ); }; wp_block_bindings_register_source( - 'post_meta', + 'core/post-meta', array( 'label' => __( 'Post Meta' ), 'apply' => $post_meta_source_callback, diff --git a/lib/compat/wordpress-6.5/blocks.php b/lib/compat/wordpress-6.5/blocks.php index e1b91364fe22e5..7888973397f6cb 100644 --- a/lib/compat/wordpress-6.5/blocks.php +++ b/lib/compat/wordpress-6.5/blocks.php @@ -76,16 +76,12 @@ function gutenberg_process_block_bindings( $block_content, $block, $block_instan * * "bindings": { * "title": { - * "source": { - * "name": "post_meta", - * "attributes": { "value": "text_custom_field" } - * } + * "source": "core/post-meta", + * "args": { "key": "text_custom_field" } * }, * "url": { - * "source": { - * "name": "post_meta", - * "attributes": { "value": "text_custom_field" } - * } + * "source": "core/post-meta", + * "args": { "key": "url_custom_field" } * } * } */ @@ -99,16 +95,16 @@ function gutenberg_process_block_bindings( $block_content, $block, $block_instan continue; } // If no source is provided, or that source is not registered, process next attribute. - if ( ! isset( $binding_source['source'] ) || ! isset( $binding_source['source']['name'] ) || ! isset( $block_bindings_sources[ $binding_source['source']['name'] ] ) ) { + if ( ! isset( $binding_source['source'] ) || ! is_string( $binding_source['source'] ) || ! isset( $block_bindings_sources[ $binding_source['source'] ] ) ) { continue; } - $source_callback = $block_bindings_sources[ $binding_source['source']['name'] ]['apply']; + $source_callback = $block_bindings_sources[ $binding_source['source'] ]['apply']; // Get the value based on the source. - if ( ! isset( $binding_source['source']['attributes'] ) ) { + if ( ! isset( $binding_source['args'] ) ) { $source_args = array(); } else { - $source_args = $binding_source['source']['attributes']; + $source_args = $binding_source['args']; } $source_value = $source_callback( $source_args, $block_instance, $binding_attribute ); // If the value is null, process next attribute. diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 69b04fe4c4904e..8e7e91dc13988a 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -160,8 +160,7 @@ export function RichTextWrapper( if ( blockTypeAttributes?.[ attribute ]?.source === 'rich-text' && - getBlockBindingsSource( args.source.name ) - ?.lockAttributesEditing + getBlockBindingsSource( args.source )?.lockAttributesEditing ) { shouldDisableEditing = true; break; diff --git a/packages/block-editor/src/hooks/use-bindings-attributes.js b/packages/block-editor/src/hooks/use-bindings-attributes.js index 94aac654097e5d..c6c847cbddcd1c 100644 --- a/packages/block-editor/src/hooks/use-bindings-attributes.js +++ b/packages/block-editor/src/hooks/use-bindings-attributes.js @@ -44,7 +44,7 @@ const createEditFunctionWithBindingsAttribute = () => Object.entries( updatedAttributes.metadata.bindings ).forEach( ( [ attributeName, settings ] ) => { const source = getBlockBindingsSource( - settings.source.name + settings.source ); if ( source ) { @@ -52,10 +52,7 @@ const createEditFunctionWithBindingsAttribute = () => const { placeholder, useValue: [ metaValue = null ] = [], - } = source.useSource( - props, - settings.source.attributes - ); + } = source.useSource( props, settings.args ); if ( placeholder && ! metaValue ) { // If the attribute is `src` or `href`, a placeholder can't be used because it is not a valid url. diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index e29b90b5e3c5c1..8ec7275c0e9f87 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -45,14 +45,14 @@ function isPartiallySynced( block ) { ) && !! block.attributes.metadata?.bindings && Object.values( block.attributes.metadata.bindings ).some( - ( binding ) => binding.source.name === 'pattern_attributes' + ( binding ) => binding.source === 'core/pattern-attributes' ) ); } function getPartiallySyncedAttributes( block ) { return Object.entries( block.attributes.metadata.bindings ) .filter( - ( [ , binding ] ) => binding.source.name === 'pattern_attributes' + ( [ , binding ] ) => binding.source === 'core/pattern-attributes' ) .map( ( [ attributeKey ] ) => attributeKey ); } diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index 4b32ad0a0ed674..c3a2aff1bd0d9f 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -245,9 +245,8 @@ function ButtonEdit( props ) { return { lockUrlControls: !! metadata?.bindings?.url && - getBlockBindingsSource( - metadata?.bindings?.url?.source?.name - )?.lockAttributesEditing === true, + getBlockBindingsSource( metadata?.bindings?.url?.source ) + ?.lockAttributesEditing === true, }; }, [ isSelected ] diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index 75db08a9ed48f3..bef3b1198a25f9 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -346,9 +346,8 @@ export function ImageEdit( { return { lockUrlControls: !! metadata?.bindings?.url && - getBlockBindingsSource( - metadata?.bindings?.url?.source?.name - )?.lockAttributesEditing === true, + getBlockBindingsSource( metadata?.bindings?.url?.source ) + ?.lockAttributesEditing === true, }; }, [ isSingleSelected ] diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index ee38ec5cfe7fd3..81193cf66e19d7 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -411,15 +411,15 @@ export default function Image( { return { lockUrlControls: !! urlBinding && - getBlockBindingsSource( urlBinding?.source?.name ) + getBlockBindingsSource( urlBinding?.source ) ?.lockAttributesEditing === true, lockAltControls: !! altBinding && - getBlockBindingsSource( altBinding?.source?.name ) + getBlockBindingsSource( altBinding?.source ) ?.lockAttributesEditing === true, lockTitleControls: !! titleBinding && - getBlockBindingsSource( titleBinding?.source?.name ) + getBlockBindingsSource( titleBinding?.source ) ?.lockAttributesEditing === true, }; }, diff --git a/packages/editor/src/bindings/post-meta.js b/packages/editor/src/bindings/post-meta.js index 17f5e1837e35e0..091491b2ed00c7 100644 --- a/packages/editor/src/bindings/post-meta.js +++ b/packages/editor/src/bindings/post-meta.js @@ -10,12 +10,12 @@ import { __ } from '@wordpress/i18n'; import { store as editorStore } from '../store'; export default { - name: 'post_meta', + name: 'core/post-meta', label: __( 'Post Meta' ), useSource( props, sourceAttributes ) { const { getCurrentPostType } = useSelect( editorStore ); const { context } = props; - const { value: metaKey } = sourceAttributes; + const { key: metaKey } = sourceAttributes; const postType = context.postType ? context.postType : getCurrentPostType(); diff --git a/packages/patterns/src/components/partial-syncing-controls.js b/packages/patterns/src/components/partial-syncing-controls.js index f5ac19bc05f3d7..0f6cde5ce7d689 100644 --- a/packages/patterns/src/components/partial-syncing-controls.js +++ b/packages/patterns/src/components/partial-syncing-controls.js @@ -19,10 +19,10 @@ function PartialSyncingControls( { name, attributes, setAttributes } ) { const syncedAttributes = PARTIAL_SYNCING_SUPPORTED_BLOCKS[ name ]; const attributeSources = Object.keys( syncedAttributes ).map( ( attributeName ) => - attributes.metadata?.bindings?.[ attributeName ]?.source?.name + attributes.metadata?.bindings?.[ attributeName ]?.source ); const isConnectedToOtherSources = attributeSources.every( - ( source ) => source && source !== 'pattern_attributes' + ( source ) => source && source !== 'core/pattern-attributes' ); // Render nothing if all supported attributes are connected to other sources. @@ -38,8 +38,8 @@ function PartialSyncingControls( { name, attributes, setAttributes } ) { if ( ! isChecked ) { for ( const attributeName of Object.keys( syncedAttributes ) ) { if ( - updatedBindings[ attributeName ]?.source?.name === - 'pattern_attributes' + updatedBindings[ attributeName ]?.source === + 'core/pattern-attributes' ) { delete updatedBindings[ attributeName ]; } @@ -59,9 +59,7 @@ function PartialSyncingControls( { name, attributes, setAttributes } ) { for ( const attributeName of Object.keys( syncedAttributes ) ) { if ( ! updatedBindings[ attributeName ] ) { updatedBindings[ attributeName ] = { - source: { - name: 'pattern_attributes', - }, + source: 'core/pattern-attributes', }; } } @@ -96,7 +94,7 @@ function PartialSyncingControls( { name, attributes, setAttributes } ) { __nextHasNoMarginBottom label={ __( 'Allow instance overrides' ) } checked={ attributeSources.some( - ( source ) => source === 'pattern_attributes' + ( source ) => source === 'core/pattern-attributes' ) } onChange={ ( isChecked ) => { updateBindings( isChecked ); diff --git a/test/e2e/specs/editor/various/pattern-overrides.spec.js b/test/e2e/specs/editor/various/pattern-overrides.spec.js index ee60091e057c02..541f2f1600ad29 100644 --- a/test/e2e/specs/editor/various/pattern-overrides.spec.js +++ b/test/e2e/specs/editor/various/pattern-overrides.spec.js @@ -92,7 +92,7 @@ test.describe( 'Pattern Overrides', () => { id: expect.any( String ), bindings: { content: { - source: { name: 'pattern_attributes' }, + source: 'core/pattern-attributes', }, }, }, @@ -222,7 +222,7 @@ test.describe( 'Pattern Overrides', () => { const paragraphId = 'paragraph-id'; const { id } = await requestUtils.createBlock( { title: 'Pattern', - content: ` + content: `

Editable

`, status: 'publish', @@ -270,7 +270,7 @@ test.describe( 'Pattern Overrides', () => { const { id } = await requestUtils.createBlock( { title: 'Pattern with overrides', content: ` -
+ `, From d45f032930e64d9016d1466521c36de37aadb34c Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 29 Jan 2024 13:47:36 +0100 Subject: [PATCH 04/45] Disable temporarily Android E2E tests (#58376) --- .github/workflows/rnmobile-android-runner.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rnmobile-android-runner.yml b/.github/workflows/rnmobile-android-runner.yml index e0a3b9639cf389..2a4ac7d0f76caa 100644 --- a/.github/workflows/rnmobile-android-runner.yml +++ b/.github/workflows/rnmobile-android-runner.yml @@ -15,7 +15,9 @@ concurrency: jobs: test: runs-on: macos-12 - if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} + # Disable for now until we fix failures in the job. + if: false + # if: ${{ github.repository == 'WordPress/gutenberg' || github.event_name == 'pull_request' }} strategy: matrix: native-test-name: [gutenberg-editor-rendering] From 44df307b1caa686b025542a1cc730a92a4eae76f Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 29 Jan 2024 13:53:17 +0100 Subject: [PATCH 05/45] Navigation: Remove use of Gutenberg_Navigation_Fallback class (#58369) --- packages/block-library/src/navigation/index.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index a0672a0bed5801..574d802b183ada 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -976,12 +976,7 @@ function block_core_navigation_get_fallback_blocks() { // If `core/page-list` is not registered then return empty blocks. $fallback_blocks = $registry->is_registered( 'core/page-list' ) ? $page_list_fallback : array(); - - if ( class_exists( 'WP_Navigation_Fallback' ) ) { - $navigation_post = WP_Navigation_Fallback::get_fallback(); - } else { - $navigation_post = Gutenberg_Navigation_Fallback::get_fallback(); - } + $navigation_post = WP_Navigation_Fallback::get_fallback(); // Use the first non-empty Navigation as fallback if available. if ( $navigation_post ) { From 8cbcc98f7872c88730e9852c344607c6b972675b Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:33:11 +0900 Subject: [PATCH 06/45] DataViews: Fix nested buttons and placeholder text in list layout (#58304) * DataViews: Fix nested buttons and placeholder text in list layout * Minor refactoring --- .../src/components/page-pages/index.js | 42 +++++++++++-------- .../page-templates-template-parts/index.js | 30 +++++++------ 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index 603a7b30de2edd..8dad4bc05039e6 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -161,6 +161,19 @@ function FeaturedImage( { item, viewType } ) { canvas: 'edit', } ); const hasMedia = !! item.featured_media; + const size = + viewType === LAYOUT_GRID + ? [ 'large', 'full', 'medium', 'thumbnail' ] + : [ 'thumbnail', 'medium', 'large', 'full' ]; + + const media = hasMedia ? ( + + ) : null; + return ( - + { viewType === LAYOUT_LIST && media } + { viewType !== LAYOUT_LIST && ( + + ) } ); } diff --git a/packages/edit-site/src/components/page-templates-template-parts/index.js b/packages/edit-site/src/components/page-templates-template-parts/index.js index a1b87d3b38d885..a6c2864ad98f3e 100644 --- a/packages/edit-site/src/components/page-templates-template-parts/index.js +++ b/packages/edit-site/src/components/page-templates-template-parts/index.js @@ -151,6 +151,7 @@ function Preview( { item, viewType } ) { postType: item.type, canvas: 'edit', } ); + const isEmpty = ! blocks?.length; // Wrap everything in a block editor provider to ensure 'styles' that are needed // for the previews are synced between the site editor store and the block editor store. @@ -165,18 +166,23 @@ function Preview( { item, viewType } ) { className={ `page-templates-preview-field is-viewtype-${ viewType }` } style={ { backgroundColor } } > - + { viewType === LAYOUT_LIST && ! isEmpty && ( + + ) } + { viewType !== LAYOUT_LIST && ( + + ) }
); From a6f1e4ac9f937f552b02bda63edc733338427e76 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Mon, 29 Jan 2024 13:36:52 +0000 Subject: [PATCH 07/45] Remove the ->ID check (#58379) --- packages/block-library/src/navigation/index.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index 574d802b183ada..70cf02e714cf89 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -1392,10 +1392,6 @@ function block_core_navigation_insert_hooked_blocks( $inner_blocks, $post = null * @param WP_Post $post Post object. */ function block_core_navigation_update_ignore_hooked_blocks_meta( $post ) { - if ( ! isset( $post->ID ) ) { - return; - } - // We run the Block Hooks mechanism so it will return the list of ignored hooked blocks // in the mock root Navigation block's metadata attribute. // We ignore the rest of the returned `$markup`; `$post->post_content` already has the hooked From d1e97fac6ce72dc36c7c2963afc64f5ca5efe541 Mon Sep 17 00:00:00 2001 From: Carolina Nymark Date: Mon, 29 Jan 2024 14:37:25 +0100 Subject: [PATCH 08/45] Post navigation link: Coding standard fixes (#58380) Update an inline comment to follow the PHP inline documentation standards. --- packages/block-library/src/post-navigation-link/index.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/post-navigation-link/index.php b/packages/block-library/src/post-navigation-link/index.php index 69c9a3f39f8a70..5bbc87ef537710 100644 --- a/packages/block-library/src/post-navigation-link/index.php +++ b/packages/block-library/src/post-navigation-link/index.php @@ -99,12 +99,12 @@ function render_block_core_post_navigation_link( $attributes, $content ) { } } - /** + /* * The dynamic portion of the function name, `$navigation_type`, * Refers to the type of adjacency, 'next' or 'previous'. * - * @See https://developer.wordpress.org/reference/functions/get_previous_post_link/ - * @See https://developer.wordpress.org/reference/functions/get_next_post_link/ + * @see https://developer.wordpress.org/reference/functions/get_previous_post_link/ + * @see https://developer.wordpress.org/reference/functions/get_next_post_link/ */ $get_link_function = "get_{$navigation_type}_post_link"; From 8585de22e8591b3b4db42f8441b1fd4242d36af1 Mon Sep 17 00:00:00 2001 From: Dean Sas Date: Mon, 29 Jan 2024 13:56:19 +0000 Subject: [PATCH 09/45] Update docblock for render_block_core_pattern (#58382) --- packages/block-library/src/pattern/index.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/block-library/src/pattern/index.php b/packages/block-library/src/pattern/index.php index 70c389e4ec8dbe..9a4d4c01b5bcd6 100644 --- a/packages/block-library/src/pattern/index.php +++ b/packages/block-library/src/pattern/index.php @@ -22,6 +22,8 @@ function register_block_core_pattern() { * * @since 6.3.0 Backwards compatibility: blocks with no `syncStatus` attribute do not receive block wrapper. * + * @global WP_Embed $wp_embed Used to process embedded content within patterns + * * @param array $attributes Block attributes. * * @return string Returns the output of the pattern. From f38f3097ec60ee6b7e589be7dc688d74fd1ec131 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Mon, 29 Jan 2024 17:24:31 +0200 Subject: [PATCH 10/45] DataViews: Fix some small issues with featured image (#58371) * DataViews: Fix some small issues with featured image * improve featured image expansion at table view * make featured image smaller in table view --- .../src/components/page-pages/index.js | 37 +++++++------ .../src/components/page-pages/style.scss | 55 ++++++++++--------- .../sidebar-dataviews/default-views.js | 2 +- 3 files changed, 49 insertions(+), 45 deletions(-) diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index 8dad4bc05039e6..73dd87eeab5a53 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classNames from 'classnames'; + /** * WordPress dependencies */ @@ -62,6 +67,9 @@ function useView( postType ) { return { ...defaultView, type: layout, + layout: { + ...( DEFAULT_CONFIG_PER_VIEW_TYPE[ layout ] || {} ), + }, }; } return defaultView; @@ -165,7 +173,6 @@ function FeaturedImage( { item, viewType } ) { viewType === LAYOUT_GRID ? [ 'large', 'full', 'medium', 'thumbnail' ] : [ 'thumbnail', 'medium', 'large', 'full' ]; - const media = hasMedia ? ( ) : null; - + if ( viewType === LAYOUT_LIST ) { + return media; + } return ( - - { viewType === LAYOUT_LIST && media } - { viewType !== LAYOUT_LIST && ( - - ) } - + { media } + ); } @@ -280,6 +282,7 @@ export default function PagePages() { ), enableSorting: false, + width: '1%', }, { header: __( 'Title' ), diff --git a/packages/edit-site/src/components/page-pages/style.scss b/packages/edit-site/src/components/page-pages/style.scss index 1125bbb1919b5a..442ea7c4928899 100644 --- a/packages/edit-site/src/components/page-pages/style.scss +++ b/packages/edit-site/src/components/page-pages/style.scss @@ -1,30 +1,3 @@ -.edit-site-page-pages__media-wrapper { - width: $grid-unit-50; - height: $grid-unit-50; - display: block; - border-radius: $grid-unit-05; - position: relative; - background-color: $gray-100; - overflow: hidden; - - .edit-site-page-pages__featured-image { - height: 100%; - object-fit: cover; - width: 100%; - } - - &::after { - border-radius: 4px; - box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); - content: ""; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; - } -} - .page-pages-preview-field__button { box-shadow: none; border: none; @@ -42,4 +15,32 @@ // Windows High Contrast mode will show this outline, but not the box-shadow. outline: 2px solid transparent; } + + &.edit-site-page-pages__media-wrapper { + width: $grid-unit-40; + height: $grid-unit-40; + display: block; + border-radius: $grid-unit-05; + position: relative; + background-color: $gray-100; + overflow: hidden; + flex-grow: 0 !important; + + .edit-site-page-pages__featured-image { + height: 100%; + object-fit: cover; + width: 100%; + } + + &::after { + border-radius: 4px; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); + content: ""; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + } + } } diff --git a/packages/edit-site/src/components/sidebar-dataviews/default-views.js b/packages/edit-site/src/components/sidebar-dataviews/default-views.js index d6e7ebe72df21a..2ea83c2863a0f7 100644 --- a/packages/edit-site/src/components/sidebar-dataviews/default-views.js +++ b/packages/edit-site/src/components/sidebar-dataviews/default-views.js @@ -42,7 +42,7 @@ const DEFAULT_PAGE_BASE = { // better to keep track of the hidden ones. hiddenFields: [ 'date', 'featured-image' ], layout: { - ...DEFAULT_CONFIG_PER_VIEW_TYPE[ LAYOUT_LIST ], + ...DEFAULT_CONFIG_PER_VIEW_TYPE[ LAYOUT_TABLE ], }, }; From bfcca1a8e731ad037f5c7fc5912918b16775117f Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Mon, 29 Jan 2024 16:34:32 +0100 Subject: [PATCH 11/45] Add allowedBlocks field to block.json to specify allowed children (#58262) * Add support for children field in block.json * canInsertBlockType: check the parentBlockType.children allowlist for insertable blocks * Migrate core/list to .children * Migrate core/navigation to .children * Update documentation * Migrate remaining Core blocks * allowedBlocks list setting is not deprecated, can be used for dynamic behavior * Rename children to allowedBlocks * Rename children to allowedBlocks in registration * canInsertBlockType: never downgrade result from boolean to null * Rename children to allowedBlocks also when patching server definitions * Add allowed_blocks to gutenberg_add_hooked_blocks * Fix typo --- .../nested-blocks-inner-blocks.md | 30 +++++++++++++++---- .../block-api/block-metadata.md | 14 +++++++++ .../block-api/block-registration.md | 14 ++++++++- lib/compat/wordpress-6.4/block-hooks.php | 1 + packages/block-editor/src/store/selectors.js | 24 ++++++++++++--- packages/block-library/src/buttons/block.json | 1 + packages/block-library/src/buttons/edit.js | 17 +++-------- .../block-library/src/buttons/edit.native.js | 8 ++--- packages/block-library/src/columns/block.json | 1 + packages/block-library/src/columns/edit.js | 12 -------- .../block-library/src/columns/edit.native.js | 12 -------- .../src/comments-pagination/block.json | 5 ++++ .../src/comments-pagination/edit.js | 6 ---- .../src/form-submit-button/block.json | 1 + .../src/form-submit-button/edit.js | 1 - packages/block-library/src/form/block.json | 9 ++++++ packages/block-library/src/form/edit.js | 11 ------- packages/block-library/src/gallery/block.json | 1 + packages/block-library/src/gallery/edit.js | 2 -- .../src/gallery/gallery.native.js | 1 - .../block-library/src/list-item/block.json | 1 + packages/block-library/src/list-item/edit.js | 1 - .../src/list-item/edit.native.js | 1 - packages/block-library/src/list/block.json | 1 + packages/block-library/src/list/edit.js | 1 - .../src/navigation-link/block.json | 5 ++++ .../block-library/src/navigation-link/edit.js | 12 ++------ .../block-library/src/navigation/block.json | 13 ++++++++ .../block-library/src/navigation/constants.js | 14 --------- .../src/navigation/edit/inner-blocks.js | 7 +---- .../navigation/edit/unsaved-inner-blocks.js | 7 +---- .../block-library/src/page-list/block.json | 1 + packages/block-library/src/page-list/edit.js | 1 - .../block-library/src/post-comment/block.json | 8 +++++ .../block-library/src/post-comment/edit.js | 9 ------ .../src/query-pagination/block.json | 5 ++++ .../src/query-pagination/edit.js | 6 ---- .../block-library/src/social-links/block.json | 1 + .../block-library/src/social-links/edit.js | 3 -- .../src/social-links/edit.native.js | 6 ---- packages/blocks/src/api/registration.js | 1 + packages/blocks/src/store/reducer.js | 14 +++++++++ schemas/json/block.json | 7 +++++ 43 files changed, 158 insertions(+), 138 deletions(-) diff --git a/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md b/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md index 94d4ea67d8cf99..b90b4668530797 100644 --- a/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md +++ b/docs/how-to-guides/block-tutorial/nested-blocks-inner-blocks.md @@ -38,14 +38,16 @@ registerBlockType( 'gutenberg-examples/example-06', { ## Allowed blocks -Using the `allowedBlocks` property, you can define the set of blocks allowed in your InnerBlock. This restricts the blocks that can be included only to those listed, all other blocks will not show in the inserter. +Using the `allowedBlocks` prop, you can further limit, in addition to the `allowedBlocks` field in `block.json`, which blocks can be inserted as direct descendants of this block. It is useful to determine the list of allowed blocks dynamically, individually for each block. For example, determined by a block attribute: ```js -const ALLOWED_BLOCKS = [ 'core/image', 'core/paragraph' ]; +const { allowedBlocks } = attributes; //... -; +; ``` +If the list of allowed blocks is always the same, prefer the [`allowedBlocks` block setting](#defining-a-children-block-relationship) instead. + ## Orientation By default, `InnerBlocks` expects its blocks to be shown in a vertical list. A valid use-case is to style inner blocks to appear horizontally, for instance by adding CSS flex or grid properties to the inner blocks wrapper. When blocks are styled in such a way, the `orientation` prop can be set to indicate that a horizontal layout is being used: @@ -109,12 +111,13 @@ add_action( 'init', function() { } ); ``` -## Using parent and ancestor relationships in blocks +## Using parent, ancestor and children relationships in blocks -A common pattern for using InnerBlocks is to create a custom block that will be only be available if its parent block is inserted. This allows builders to establish a relationship between blocks, while limiting a nested block's discoverability. Currently, there are two relationships builders can use: `parent` and `ancestor`. The differences are: +A common pattern for using InnerBlocks is to create a custom block that will only be available if its parent block is inserted. This allows builders to establish a relationship between blocks, while limiting a nested block's discoverability. There are three relationships that builders can use: `parent`, `ancestor` and `allowedBlocks`. The differences are: - If you assign a `parent` then you’re stating that the nested block can only be used and inserted as a __direct descendant of the parent__. - If you assign an `ancestor` then you’re stating that the nested block can only be used and inserted as a __descendent of the parent__. +- If you assign the `allowedBlocks` then you’re stating a relationship in the opposite direction, i.e., which blocks can be used and inserted as __direct descendants of this block__. The key difference between `parent` and `ancestor` is `parent` has finer specificity, while an `ancestor` has greater flexibility in its nested hierarchy. @@ -150,6 +153,23 @@ When defining a descendent block, use the `ancestor` block setting. This prevent } ``` +### Defining a children block relationship + +An example of this is the Navigation block, which is assigned the `allowedBlocks` block setting. This makes only a certain subset of block types to be available as direct descendants of the Navigation block. See [Navigation code for reference](https://github.com/WordPress/gutenberg/tree/HEAD/packages/block-library/src/navigation). + +The `allowedBlocks` setting can be extended by builders of custom blocks. The custom block can hook into the `blocks.registerBlockType` filter and add itself to the available children of the Navigation. + +When defining a set of possible descendant blocks, use the `allowedBlocks` block setting. This limits what blocks are showing in the inserter when inserting a new child block. + +```json +{ + "title": "Navigation", + "name": "core/navigation", + "allowedBlocks": [ "core/navigation-link", "core/search", "core/social-links", "core/page-list", "core/spacer" ], + // ... +} +``` + ## Using a React hook You can use a react hook called `useInnerBlocksProps` instead of the `InnerBlocks` component. This hook allows you to take more control over the markup of inner blocks areas. diff --git a/docs/reference-guides/block-api/block-metadata.md b/docs/reference-guides/block-api/block-metadata.md index f89257f52d0446..6e786a2ff6322c 100644 --- a/docs/reference-guides/block-api/block-metadata.md +++ b/docs/reference-guides/block-api/block-metadata.md @@ -186,6 +186,20 @@ Setting `parent` lets a block require that it is only available when nested with The `ancestor` property makes a block available inside the specified block types at any position of the ancestor block subtree. That allows, for example, to place a ‘Comment Content’ block inside a ‘Column’ block, as long as ‘Column’ is somewhere within a ‘Comment Template’ block. In comparison to the `parent` property blocks that specify their `ancestor` can be placed anywhere in the subtree whilst blocks with a specified `parent` need to be direct children. +### Allowed Blocks + +- Type: `string[]` +- Optional +- Localized: No +- Property: `allowedBlocks` +- Since: `WordPress 6.5.0` + +```json +{ "allowedBlocks": [ "my-block/product" ] } +``` + +The `allowedBlocks` specifies which block types can be the direct children of the block. For example, a ‘List’ block can allow only ‘List Item’ blocks as children. + ### Icon - Type: `string` diff --git a/docs/reference-guides/block-api/block-registration.md b/docs/reference-guides/block-api/block-registration.md index bec9bbd871cbae..7af276d242509a 100644 --- a/docs/reference-guides/block-api/block-registration.md +++ b/docs/reference-guides/block-api/block-registration.md @@ -275,7 +275,19 @@ The `ancestor` property makes a block available inside the specified block types ancestor: [ 'core/columns' ], ``` -#### Block Hooks (optional) +#### allowedBlocks (optional) + +- **Type:** `Array` +- **Since**: `WordPress 6.5.0` + +Setting the `allowedBlocks` property will limit which block types can be nested as direct children of the block. + +```js +// Only allow the Columns block to be nested as direct child of this block +allowedBlocks: [ 'core/columns' ], +``` + +#### blockHooks (optional) - **Type:** `Object` - **Since**: `WordPress 6.4.0` diff --git a/lib/compat/wordpress-6.4/block-hooks.php b/lib/compat/wordpress-6.4/block-hooks.php index 9cf6d2414926a5..46115d5b6c629c 100644 --- a/lib/compat/wordpress-6.4/block-hooks.php +++ b/lib/compat/wordpress-6.4/block-hooks.php @@ -73,6 +73,7 @@ function gutenberg_add_hooked_blocks( $settings, $metadata ) { 'keywords' => 'keywords', 'example' => 'example', 'variations' => 'variations', + 'allowed_blocks' => 'allowedBlocks', ); // Add `block_hooks` to the list of fields to pick. $fields_to_pick['block_hooks'] = 'blockHooks'; diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 0f6f7c33a91fa0..7964f86a5e782c 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1563,14 +1563,30 @@ const canInsertBlockTypeUnmemoized = ( return false; } - const parentAllowedBlocks = parentBlockListSettings?.allowedBlocks; - const hasParentAllowedBlock = checkAllowList( - parentAllowedBlocks, + const parentName = getBlockName( state, rootClientId ); + const parentBlockType = getBlockType( parentName ); + + // Look at the `blockType.allowedBlocks` field to determine whether this is an allowed child block. + const parentAllowedChildBlocks = parentBlockType?.allowedBlocks; + let hasParentAllowedBlock = checkAllowList( + parentAllowedChildBlocks, blockName ); + // The `allowedBlocks` block list setting can further limit which blocks are allowed children. + if ( hasParentAllowedBlock !== false ) { + const parentAllowedBlocks = parentBlockListSettings?.allowedBlocks; + const hasParentListAllowedBlock = checkAllowList( + parentAllowedBlocks, + blockName + ); + // Never downgrade the result from `true` to `null` + if ( hasParentListAllowedBlock !== null ) { + hasParentAllowedBlock = hasParentListAllowedBlock; + } + } + const blockAllowedParentBlocks = blockType.parent; - const parentName = getBlockName( state, rootClientId ); const hasBlockAllowedParent = checkAllowList( blockAllowedParentBlocks, parentName diff --git a/packages/block-library/src/buttons/block.json b/packages/block-library/src/buttons/block.json index 4dc420bd418855..fde85ae72a316d 100644 --- a/packages/block-library/src/buttons/block.json +++ b/packages/block-library/src/buttons/block.json @@ -4,6 +4,7 @@ "name": "core/buttons", "title": "Buttons", "category": "design", + "allowedBlocks": [ "core/button" ], "description": "Prompt visitors to take action with a group of button-style links.", "keywords": [ "link" ], "textdomain": "default", diff --git a/packages/block-library/src/buttons/edit.js b/packages/block-library/src/buttons/edit.js index dce160dfb2fea8..0b9d2fe148ddd8 100644 --- a/packages/block-library/src/buttons/edit.js +++ b/packages/block-library/src/buttons/edit.js @@ -14,15 +14,8 @@ import { import { useSelect } from '@wordpress/data'; import { store as blocksStore } from '@wordpress/blocks'; -/** - * Internal dependencies - */ -import { name as buttonBlockName } from '../button'; - -const ALLOWED_BLOCKS = [ buttonBlockName ]; - const DEFAULT_BLOCK = { - name: buttonBlockName, + name: 'core/button', attributesToCopy: [ 'backgroundColor', 'border', @@ -48,24 +41,22 @@ function ButtonsEdit( { attributes, className } ) { select( blockEditorStore ).getSettings() .__experimentalPreferredStyleVariations; const buttonVariations = select( blocksStore ).getBlockVariations( - buttonBlockName, + 'core/button', 'inserter' ); return { - preferredStyle: - preferredStyleVariations?.value?.[ buttonBlockName ], + preferredStyle: preferredStyleVariations?.value?.[ 'core/button' ], hasButtonVariations: buttonVariations.length > 0, }; }, [] ); const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks: ALLOWED_BLOCKS, defaultBlock: DEFAULT_BLOCK, // This check should be handled by the `Inserter` internally to be consistent across all blocks that use it. directInsert: ! hasButtonVariations, template: [ [ - buttonBlockName, + 'core/button', { className: preferredStyle && `is-style-${ preferredStyle }` }, ], ], diff --git a/packages/block-library/src/buttons/edit.native.js b/packages/block-library/src/buttons/edit.native.js index bb73d085a8f481..821d6d1932ecc5 100644 --- a/packages/block-library/src/buttons/edit.native.js +++ b/packages/block-library/src/buttons/edit.native.js @@ -21,11 +21,8 @@ import { alignmentHelpers } from '@wordpress/components'; /** * Internal dependencies */ -import { name as buttonBlockName } from '../button/'; import styles from './editor.scss'; -const ALLOWED_BLOCKS = [ buttonBlockName ]; - const layoutProp = { type: 'default', alignments: [] }; const POPOVER_PROPS = { @@ -76,7 +73,7 @@ export default function ButtonsEdit( { const preferredStyleVariations = select( blockEditorStore ).getSettings() .__experimentalPreferredStyleVariations; - return preferredStyleVariations?.value?.[ buttonBlockName ]; + return preferredStyleVariations?.value?.[ 'core/button' ]; }, [] ); const { getBlockOrder } = useSelect( blockEditorStore ); @@ -147,10 +144,9 @@ export default function ButtonsEdit( { ) } { resizeObserver } 1 ? 'horizontal' : undefined } horizontal={ columnsInRow > 1 } - allowedBlocks={ ALLOWED_BLOCKS } contentResizeMode="stretch" onAddBlock={ onAddBlock } onDeleteBlock={ diff --git a/packages/block-library/src/comments-pagination/block.json b/packages/block-library/src/comments-pagination/block.json index a11decd201e94f..e4a1a3c3a15d97 100644 --- a/packages/block-library/src/comments-pagination/block.json +++ b/packages/block-library/src/comments-pagination/block.json @@ -5,6 +5,11 @@ "title": "Comments Pagination", "category": "theme", "parent": [ "core/comments" ], + "allowedBlocks": [ + "core/comments-pagination-previous", + "core/comments-pagination-numbers", + "core/comments-pagination-next" + ], "description": "Displays a paginated navigation to next/previous set of comments, when applicable.", "textdomain": "default", "attributes": { diff --git a/packages/block-library/src/comments-pagination/edit.js b/packages/block-library/src/comments-pagination/edit.js index 9837dda14e6810..aba29233064978 100644 --- a/packages/block-library/src/comments-pagination/edit.js +++ b/packages/block-library/src/comments-pagination/edit.js @@ -22,11 +22,6 @@ const TEMPLATE = [ [ 'core/comments-pagination-numbers' ], [ 'core/comments-pagination-next' ], ]; -const ALLOWED_BLOCKS = [ - 'core/comments-pagination-previous', - 'core/comments-pagination-numbers', - 'core/comments-pagination-next', -]; export default function QueryPaginationEdit( { attributes: { paginationArrow }, @@ -52,7 +47,6 @@ export default function QueryPaginationEdit( { const blockProps = useBlockProps(); const innerBlocksProps = useInnerBlocksProps( blockProps, { template: TEMPLATE, - allowedBlocks: ALLOWED_BLOCKS, } ); // Get the Discussion settings diff --git a/packages/block-library/src/form-submit-button/block.json b/packages/block-library/src/form-submit-button/block.json index b3fbd0ccee69c2..97ced49e70b22a 100644 --- a/packages/block-library/src/form-submit-button/block.json +++ b/packages/block-library/src/form-submit-button/block.json @@ -7,6 +7,7 @@ "category": "common", "icon": "button", "ancestor": [ "core/form" ], + "allowedBlocks": [ "core/buttons", "core/button" ], "description": "A submission button for forms.", "keywords": [ "submit", "button", "form" ], "textdomain": "default", diff --git a/packages/block-library/src/form-submit-button/edit.js b/packages/block-library/src/form-submit-button/edit.js index 4b22b26fd4755c..e2bcbe067c70ca 100644 --- a/packages/block-library/src/form-submit-button/edit.js +++ b/packages/block-library/src/form-submit-button/edit.js @@ -23,7 +23,6 @@ const TEMPLATE = [ const Edit = () => { const blockProps = useBlockProps(); const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks: TEMPLATE, template: TEMPLATE, templateLock: 'all', } ); diff --git a/packages/block-library/src/form/block.json b/packages/block-library/src/form/block.json index 0c6451f4959a48..fa5212822cc71e 100644 --- a/packages/block-library/src/form/block.json +++ b/packages/block-library/src/form/block.json @@ -5,6 +5,15 @@ "name": "core/form", "title": "Form", "category": "common", + "allowedBlocks": [ + "core/paragraph", + "core/heading", + "core/form-input", + "core/form-submit-button", + "core/form-submission-notification", + "core/group", + "core/columns" + ], "description": "A form.", "keywords": [ "container", "wrapper", "row", "section" ], "textdomain": "default", diff --git a/packages/block-library/src/form/edit.js b/packages/block-library/src/form/edit.js index 7fded64837de9d..bea513a3785dcd 100644 --- a/packages/block-library/src/form/edit.js +++ b/packages/block-library/src/form/edit.js @@ -20,16 +20,6 @@ import { formSubmissionNotificationError, } from './utils.js'; -const ALLOWED_BLOCKS = [ - 'core/paragraph', - 'core/heading', - 'core/form-input', - 'core/form-submit-button', - 'core/form-submission-notification', - 'core/group', - 'core/columns', -]; - const TEMPLATE = [ formSubmissionNotificationSuccess, formSubmissionNotificationError, @@ -76,7 +66,6 @@ const Edit = ( { attributes, setAttributes, clientId } ) => { ); const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks: ALLOWED_BLOCKS, template: TEMPLATE, renderAppender: hasInnerBlocks ? undefined diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json index a5425c55381f94..bfeeb792a7fa02 100644 --- a/packages/block-library/src/gallery/block.json +++ b/packages/block-library/src/gallery/block.json @@ -4,6 +4,7 @@ "name": "core/gallery", "title": "Gallery", "category": "media", + "allowedBlocks": [ "core/image" ], "description": "Display multiple images in a rich gallery.", "keywords": [ "images", "photos" ], "textdomain": "default", diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 4a646ce8362233..e2a836ae35f6cc 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -64,7 +64,6 @@ const linkOptions = [ }, ]; const ALLOWED_MEDIA_TYPES = [ 'image' ]; -const allowedBlocks = [ 'core/image' ]; const PLACEHOLDER_TEXT = Platform.isNative ? __( 'Add media' ) @@ -512,7 +511,6 @@ function GalleryEdit( props ) { }; const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks, orientation: 'horizontal', renderAppender: false, ...nativeInnerBlockProps, diff --git a/packages/block-library/src/gallery/gallery.native.js b/packages/block-library/src/gallery/gallery.native.js index 8979d814a8da00..49531325247b7e 100644 --- a/packages/block-library/src/gallery/gallery.native.js +++ b/packages/block-library/src/gallery/gallery.native.js @@ -64,7 +64,6 @@ export const Gallery = ( props ) => { {}, { contentResizeMode: 'stretch', - allowedBlocks: [ 'core/image' ], orientation: 'horizontal', renderAppender: false, numColumns: displayedColumns, diff --git a/packages/block-library/src/list-item/block.json b/packages/block-library/src/list-item/block.json index 06997c2ac23f8e..0857aaac45d9a2 100644 --- a/packages/block-library/src/list-item/block.json +++ b/packages/block-library/src/list-item/block.json @@ -5,6 +5,7 @@ "title": "List item", "category": "text", "parent": [ "core/list" ], + "allowedBlocks": [ "core/list" ], "description": "Create a list item.", "textdomain": "default", "attributes": { diff --git a/packages/block-library/src/list-item/edit.js b/packages/block-library/src/list-item/edit.js index 7733a762807528..46cbd3a94831d5 100644 --- a/packages/block-library/src/list-item/edit.js +++ b/packages/block-library/src/list-item/edit.js @@ -81,7 +81,6 @@ export default function ListItemEdit( { const { placeholder, content } = attributes; const blockProps = useBlockProps( { ref: useCopy( clientId ) } ); const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks: [ 'core/list' ], renderAppender: false, __unstableDisableDropZone: true, } ); diff --git a/packages/block-library/src/list-item/edit.native.js b/packages/block-library/src/list-item/edit.native.js index cf2e77c08d2e83..854cc8ce15214b 100644 --- a/packages/block-library/src/list-item/edit.native.js +++ b/packages/block-library/src/list-item/edit.native.js @@ -90,7 +90,6 @@ export default function ListItemEdit( { } ); const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks: [ 'core/list' ], renderAppender: false, } ); diff --git a/packages/block-library/src/list/block.json b/packages/block-library/src/list/block.json index e2fb9e4c9e3b0d..a7dcf36cf4ad74 100644 --- a/packages/block-library/src/list/block.json +++ b/packages/block-library/src/list/block.json @@ -4,6 +4,7 @@ "name": "core/list", "title": "List", "category": "text", + "allowedBlocks": [ "core/list-item" ], "description": "Create a bulleted or numbered list.", "keywords": [ "bullet list", "ordered list", "numbered list" ], "textdomain": "default", diff --git a/packages/block-library/src/list/edit.js b/packages/block-library/src/list/edit.js index e1d29d517a5ffe..ad294de93e77b1 100644 --- a/packages/block-library/src/list/edit.js +++ b/packages/block-library/src/list/edit.js @@ -125,7 +125,6 @@ export default function Edit( { attributes, setAttributes, clientId, style } ) { } ); const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks: [ 'core/list-item' ], template: TEMPLATE, templateLock: false, templateInsertUpdatesSelection: true, diff --git a/packages/block-library/src/navigation-link/block.json b/packages/block-library/src/navigation-link/block.json index d8f2fe31aef9dd..94a11217d51393 100644 --- a/packages/block-library/src/navigation-link/block.json +++ b/packages/block-library/src/navigation-link/block.json @@ -5,6 +5,11 @@ "title": "Custom Link", "category": "design", "parent": [ "core/navigation" ], + "allowedBlocks": [ + "core/navigation-link", + "core/navigation-submenu", + "core/page-list" + ], "description": "Add a page, link, or another item to your navigation.", "textdomain": "default", "attributes": { diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index f6936e8ecfe362..ecf170c3697f78 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -45,6 +45,8 @@ import { LinkUI } from './link-ui'; import { updateAttributes } from './update-attributes'; import { getColors } from '../navigation/edit/utils'; +const DEFAULT_BLOCK = { name: 'core/navigation-link' }; + /** * A React hook to determine if it's dragging within the target element. * @@ -355,22 +357,12 @@ export default function NavigationLinkEdit( { onKeyDown, } ); - const ALLOWED_BLOCKS = [ - 'core/navigation-link', - 'core/navigation-submenu', - 'core/page-list', - ]; - const DEFAULT_BLOCK = { - name: 'core/navigation-link', - }; - const innerBlocksProps = useInnerBlocksProps( { ...blockProps, className: 'remove-outline', // Remove the outline from the inner blocks container. }, { - allowedBlocks: ALLOWED_BLOCKS, defaultBlock: DEFAULT_BLOCK, directInsert: true, renderAppender: false, diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json index 36817a5e1c35b1..eef6af390de78a 100644 --- a/packages/block-library/src/navigation/block.json +++ b/packages/block-library/src/navigation/block.json @@ -4,6 +4,19 @@ "name": "core/navigation", "title": "Navigation", "category": "theme", + "allowedBlocks": [ + "core/navigation-link", + "core/search", + "core/social-links", + "core/page-list", + "core/spacer", + "core/home-link", + "core/site-title", + "core/site-logo", + "core/navigation-submenu", + "core/loginout", + "core/buttons" + ], "description": "A collection of blocks that allow visitors to get around your site.", "keywords": [ "menu", "navigation", "links" ], "textdomain": "default", diff --git a/packages/block-library/src/navigation/constants.js b/packages/block-library/src/navigation/constants.js index c712bc4000c36d..ff13309d1e4e78 100644 --- a/packages/block-library/src/navigation/constants.js +++ b/packages/block-library/src/navigation/constants.js @@ -2,20 +2,6 @@ export const DEFAULT_BLOCK = { name: 'core/navigation-link', }; -export const ALLOWED_BLOCKS = [ - 'core/navigation-link', - 'core/search', - 'core/social-links', - 'core/page-list', - 'core/spacer', - 'core/home-link', - 'core/site-title', - 'core/site-logo', - 'core/navigation-submenu', - 'core/loginout', - 'core/buttons', -]; - export const PRIORITIZED_INSERTER_BLOCKS = [ 'core/navigation-link/page', 'core/navigation-link', diff --git a/packages/block-library/src/navigation/edit/inner-blocks.js b/packages/block-library/src/navigation/edit/inner-blocks.js index 19258213f26e5f..4bb5429cf8c04a 100644 --- a/packages/block-library/src/navigation/edit/inner-blocks.js +++ b/packages/block-library/src/navigation/edit/inner-blocks.js @@ -14,11 +14,7 @@ import { useMemo } from '@wordpress/element'; * Internal dependencies */ import PlaceholderPreview from './placeholder/placeholder-preview'; -import { - DEFAULT_BLOCK, - ALLOWED_BLOCKS, - PRIORITIZED_INSERTER_BLOCKS, -} from '../constants'; +import { DEFAULT_BLOCK, PRIORITIZED_INSERTER_BLOCKS } from '../constants'; export default function NavigationInnerBlocks( { clientId, @@ -96,7 +92,6 @@ export default function NavigationInnerBlocks( { value: blocks, onInput, onChange, - allowedBlocks: ALLOWED_BLOCKS, prioritizedInserterBlocks: PRIORITIZED_INSERTER_BLOCKS, defaultBlock: DEFAULT_BLOCK, directInsert: shouldDirectInsert, diff --git a/packages/block-library/src/navigation/edit/unsaved-inner-blocks.js b/packages/block-library/src/navigation/edit/unsaved-inner-blocks.js index e2ad08c2a99ab1..61c5eb32aed16f 100644 --- a/packages/block-library/src/navigation/edit/unsaved-inner-blocks.js +++ b/packages/block-library/src/navigation/edit/unsaved-inner-blocks.js @@ -11,11 +11,7 @@ import { useContext, useEffect, useRef, useMemo } from '@wordpress/element'; * Internal dependencies */ import { areBlocksDirty } from './are-blocks-dirty'; -import { - DEFAULT_BLOCK, - ALLOWED_BLOCKS, - SELECT_NAVIGATION_MENUS_ARGS, -} from '../constants'; +import { DEFAULT_BLOCK, SELECT_NAVIGATION_MENUS_ARGS } from '../constants'; const EMPTY_OBJECT = {}; @@ -67,7 +63,6 @@ export default function UnsavedInnerBlocks( { }, { renderAppender: hasSelection ? undefined : false, - allowedBlocks: ALLOWED_BLOCKS, defaultBlock: DEFAULT_BLOCK, directInsert: shouldDirectInsert, } diff --git a/packages/block-library/src/page-list/block.json b/packages/block-library/src/page-list/block.json index 7f4f2ce86c425f..5035d5611e32e7 100644 --- a/packages/block-library/src/page-list/block.json +++ b/packages/block-library/src/page-list/block.json @@ -4,6 +4,7 @@ "name": "core/page-list", "title": "Page List", "category": "widgets", + "allowedBlocks": [ "core/page-list-item" ], "description": "Display a list of all pages.", "keywords": [ "menu", "navigation" ], "textdomain": "default", diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js index 8f21f781396c6f..34b35d61a7fa65 100644 --- a/packages/block-library/src/page-list/edit.js +++ b/packages/block-library/src/page-list/edit.js @@ -286,7 +286,6 @@ export default function PageListEdit( { } ); const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks: [ 'core/page-list-item' ], renderAppender: false, __unstableDisableDropZone: true, templateLock: isChildOfNavigation ? false : 'all', diff --git a/packages/block-library/src/post-comment/block.json b/packages/block-library/src/post-comment/block.json index 85bdb7dd6cf327..558f0e3496eff8 100644 --- a/packages/block-library/src/post-comment/block.json +++ b/packages/block-library/src/post-comment/block.json @@ -5,6 +5,14 @@ "name": "core/post-comment", "title": "Comment (deprecated)", "category": "theme", + "allowedBlocks": [ + "core/avatar", + "core/comment-author-name", + "core/comment-content", + "core/comment-date", + "core/comment-edit-link", + "core/comment-reply-link" + ], "description": "This block is deprecated. Please use the Comments block instead.", "textdomain": "default", "attributes": { diff --git a/packages/block-library/src/post-comment/edit.js b/packages/block-library/src/post-comment/edit.js index 35c0eee93308f4..54ad72be30d7c0 100644 --- a/packages/block-library/src/post-comment/edit.js +++ b/packages/block-library/src/post-comment/edit.js @@ -7,14 +7,6 @@ import { useState } from '@wordpress/element'; import { blockDefault } from '@wordpress/icons'; import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor'; -const ALLOWED_BLOCKS = [ - 'core/avatar', - 'core/comment-author-name', - 'core/comment-content', - 'core/comment-date', - 'core/comment-edit-link', - 'core/comment-reply-link', -]; const TEMPLATE = [ [ 'core/avatar' ], [ 'core/comment-author-name' ], @@ -29,7 +21,6 @@ export default function Edit( { attributes: { commentId }, setAttributes } ) { const blockProps = useBlockProps(); const innerBlocksProps = useInnerBlocksProps( blockProps, { template: TEMPLATE, - allowedBlocks: ALLOWED_BLOCKS, } ); if ( ! commentId ) { diff --git a/packages/block-library/src/query-pagination/block.json b/packages/block-library/src/query-pagination/block.json index e32a9ba9b495ff..973e9486bce6a0 100644 --- a/packages/block-library/src/query-pagination/block.json +++ b/packages/block-library/src/query-pagination/block.json @@ -5,6 +5,11 @@ "title": "Pagination", "category": "theme", "parent": [ "core/query" ], + "allowedBlocks": [ + "core/query-pagination-previous", + "core/query-pagination-numbers", + "core/query-pagination-next" + ], "description": "Displays a paginated navigation to next/previous set of posts, when applicable.", "textdomain": "default", "attributes": { diff --git a/packages/block-library/src/query-pagination/edit.js b/packages/block-library/src/query-pagination/edit.js index 7598eba5c1cacf..e051c2e67e7e5a 100644 --- a/packages/block-library/src/query-pagination/edit.js +++ b/packages/block-library/src/query-pagination/edit.js @@ -23,11 +23,6 @@ const TEMPLATE = [ [ 'core/query-pagination-numbers' ], [ 'core/query-pagination-next' ], ]; -const ALLOWED_BLOCKS = [ - 'core/query-pagination-previous', - 'core/query-pagination-numbers', - 'core/query-pagination-next', -]; export default function QueryPaginationEdit( { attributes: { paginationArrow, showLabel }, @@ -54,7 +49,6 @@ export default function QueryPaginationEdit( { const blockProps = useBlockProps(); const innerBlocksProps = useInnerBlocksProps( blockProps, { template: TEMPLATE, - allowedBlocks: ALLOWED_BLOCKS, } ); // Always show label text if paginationArrow is set to 'none'. useEffect( () => { diff --git a/packages/block-library/src/social-links/block.json b/packages/block-library/src/social-links/block.json index 20206511a4c96c..1aea3684d0c63d 100644 --- a/packages/block-library/src/social-links/block.json +++ b/packages/block-library/src/social-links/block.json @@ -4,6 +4,7 @@ "name": "core/social-links", "title": "Social Icons", "category": "widgets", + "allowedBlocks": [ "core/social-link" ], "description": "Display icons linking to your social media profiles or sites.", "keywords": [ "links" ], "textdomain": "default", diff --git a/packages/block-library/src/social-links/edit.js b/packages/block-library/src/social-links/edit.js index 03bcd59573aec2..38a68e7f2dab2c 100644 --- a/packages/block-library/src/social-links/edit.js +++ b/packages/block-library/src/social-links/edit.js @@ -27,8 +27,6 @@ import { import { __ } from '@wordpress/i18n'; import { check } from '@wordpress/icons'; -const ALLOWED_BLOCKS = [ 'core/social-link' ]; - const sizeOptions = [ { name: __( 'Small' ), value: 'has-small-icon-size' }, { name: __( 'Normal' ), value: 'has-normal-icon-size' }, @@ -106,7 +104,6 @@ export function SocialLinksEdit( props ) { const blockProps = useBlockProps( { className } ); const innerBlocksProps = useInnerBlocksProps( blockProps, { - allowedBlocks: ALLOWED_BLOCKS, placeholder: isSelected ? SelectedSocialPlaceholder : SocialPlaceholder, templateLock: false, orientation: attributes.layout?.orientation ?? 'horizontal', diff --git a/packages/block-library/src/social-links/edit.native.js b/packages/block-library/src/social-links/edit.native.js index 8236eef0814775..f49a672a7de8ff 100644 --- a/packages/block-library/src/social-links/edit.native.js +++ b/packages/block-library/src/social-links/edit.native.js @@ -17,11 +17,6 @@ import { compose, usePreferredColorSchemeStyle } from '@wordpress/compose'; * Internal dependencies */ import styles from './editor.scss'; -import variations from '../social-link/variations'; - -const ALLOWED_BLOCKS = variations.map( - ( v ) => `core/social-link-${ v.name }` -); // Template contains the links that show when start. const TEMPLATE = [ @@ -95,7 +90,6 @@ function SocialLinksEdit( { return ( = 6.5. + if ( + serverDefinition.allowedBlocks === undefined && + blockType.allowedBlocks + ) { + newDefinition = { + ...serverDefinition, + ...newDefinition, + allowedBlocks: blockType.allowedBlocks, + }; + } } else { newDefinition = Object.fromEntries( Object.entries( blockType ) diff --git a/schemas/json/block.json b/schemas/json/block.json index 5658361d7aab64..b75a7b295fe293 100644 --- a/schemas/json/block.json +++ b/schemas/json/block.json @@ -73,6 +73,13 @@ "type": "string" } }, + "allowedBlocks": { + "type": "array", + "description": "The `allowedBlocks` property specifies that only the listed block types can be the children of this block. For example, a ‘List’ block allows only ‘List Item’ blocks as direct children.", + "items": { + "type": "string" + } + }, "icon": { "type": "string", "description": "An icon property should be specified to make it easier to identify a block. These can be any of WordPress’ Dashicons (slug serving also as a fallback in non-js contexts)." From e1624cf9f9c7fc43cdabf8bbaef0da6af5830282 Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Mon, 29 Jan 2024 15:46:13 +0000 Subject: [PATCH 12/45] Separator: Remove border-bottom property (#55725) --- packages/block-library/src/separator/style.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/separator/style.scss b/packages/block-library/src/separator/style.scss index e7965c8efe4daf..84622211e5a207 100644 --- a/packages/block-library/src/separator/style.scss +++ b/packages/block-library/src/separator/style.scss @@ -1,11 +1,11 @@ .wp-block-separator { - border-top: 1px solid currentColor; - border-bottom: 1px solid currentColor; + border-top: 2px solid currentColor; // Default, thin style, is stored in theme.scss so it can be opted out of - // Unset the left and right borders by default, otherwise some browsers will render them as "inset". + // Unset the left, right and bottom borders by default, otherwise some browsers will render them as "inset". border-left: none; border-right: none; + border-bottom: none; // Dots style &.is-style-dots { From 7e7f042547132269615e4ae9d9792df97c4999bf Mon Sep 17 00:00:00 2001 From: Tom Cafferkey Date: Mon, 29 Jan 2024 16:23:29 +0000 Subject: [PATCH 13/45] Block Hooks API: Update Navigation block feature gate (#58388) --- packages/block-library/src/navigation/index.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index 70cf02e714cf89..2c7a818cc4f4c3 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -192,7 +192,7 @@ private static function get_inner_blocks_from_navigation_post( $attributes ) { // it encounters whitespace. This code strips it. $blocks = block_core_navigation_filter_out_empty_blocks( $parsed_blocks ); - if ( function_exists( 'get_hooked_blocks' ) ) { + if ( function_exists( 'get_hooked_block_markup' ) ) { // Run Block Hooks algorithm to inject hooked blocks. $markup = block_core_navigation_insert_hooked_blocks( $blocks, $navigation_post ); $root_nav_block = parse_blocks( $markup )[0]; @@ -987,7 +987,7 @@ function block_core_navigation_get_fallback_blocks() { // In this case default to the (Page List) fallback. $fallback_blocks = ! empty( $maybe_fallback ) ? $maybe_fallback : $fallback_blocks; - if ( function_exists( 'get_hooked_blocks' ) ) { + if ( function_exists( 'get_hooked_block_markup' ) ) { // Run Block Hooks algorithm to inject hooked blocks. // We have to run it here because we need the post ID of the Navigation block to track ignored hooked blocks. $markup = block_core_navigation_insert_hooked_blocks( $fallback_blocks, $navigation_post ); @@ -1413,9 +1413,9 @@ function block_core_navigation_update_ignore_hooked_blocks_meta( $post ) { } } -// Injection of hooked blocks into the Navigation block relies on some functions present in WP >= 6.4 -// that are not present in Gutenberg's WP 6.4 compatibility layer. -if ( function_exists( 'get_hooked_blocks' ) ) { +// Injection of hooked blocks into the Navigation block relies on some functions present in WP >= 6.5 +// that are not present in Gutenberg's WP 6.5 compatibility layer. +if ( function_exists( 'get_hooked_block_markup' ) ) { add_action( 'rest_insert_wp_navigation', 'block_core_navigation_update_ignore_hooked_blocks_meta', 10, 3 ); } @@ -1445,8 +1445,8 @@ function block_core_navigation_insert_hooked_blocks_into_rest_response( $respons return $response; } -// Injection of hooked blocks into the Navigation block relies on some functions present in WP >= 6.4 -// that are not present in Gutenberg's WP 6.4 compatibility layer. -if ( function_exists( 'get_hooked_blocks' ) ) { +// Injection of hooked blocks into the Navigation block relies on some functions present in WP >= 6.5 +// that are not present in Gutenberg's WP 6.5 compatibility layer. +if ( function_exists( 'get_hooked_block_markup' ) ) { add_filter( 'rest_prepare_wp_navigation', 'block_core_navigation_insert_hooked_blocks_into_rest_response', 10, 3 ); } From 2569121b896ec18db4ce4262d6bbfbac72b32045 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Mon, 29 Jan 2024 18:34:11 +0200 Subject: [PATCH 14/45] useSettings: extract selector (#58354) --- .../components/global-styles/effects-panel.js | 2 +- .../global-styles/typography-panel.js | 2 +- .../src/components/inner-blocks/index.js | 38 +-- .../src/components/use-settings/index.js | 241 +----------------- .../src/components/use-settings/test/index.js | 141 ---------- .../src/store/get-block-settings.js | 235 +++++++++++++++++ .../src/store/private-selectors.js | 8 +- .../src/store/test/get-block-settings.js | 90 +++++++ 8 files changed, 362 insertions(+), 395 deletions(-) delete mode 100644 packages/block-editor/src/components/use-settings/test/index.js create mode 100644 packages/block-editor/src/store/get-block-settings.js create mode 100644 packages/block-editor/src/store/test/get-block-settings.js diff --git a/packages/block-editor/src/components/global-styles/effects-panel.js b/packages/block-editor/src/components/global-styles/effects-panel.js index 94c1d119c354c9..4fefed8e3497e3 100644 --- a/packages/block-editor/src/components/global-styles/effects-panel.js +++ b/packages/block-editor/src/components/global-styles/effects-panel.js @@ -26,7 +26,7 @@ import { shadow as shadowIcon, Icon, check } from '@wordpress/icons'; /** * Internal dependencies */ -import { mergeOrigins } from '../use-settings'; +import { mergeOrigins } from '../../store/get-block-settings'; import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils'; import { setImmutably } from '../../utils/object'; diff --git a/packages/block-editor/src/components/global-styles/typography-panel.js b/packages/block-editor/src/components/global-styles/typography-panel.js index 668e8b101be926..cc8b0589644bd5 100644 --- a/packages/block-editor/src/components/global-styles/typography-panel.js +++ b/packages/block-editor/src/components/global-styles/typography-panel.js @@ -13,7 +13,7 @@ import { useCallback } from '@wordpress/element'; /** * Internal dependencies */ -import { mergeOrigins, hasMergedOrigins } from '../use-settings'; +import { mergeOrigins, hasMergedOrigins } from '../../store/get-block-settings'; import FontFamilyControl from '../font-family'; import FontAppearanceControl from '../font-appearance-control'; import LineHeightControl from '../line-height-control'; diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 4bb555d27f626c..442f2568ce73dd 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -29,7 +29,7 @@ import { useBlockEditContext } from '../block-edit/context'; import useBlockSync from '../provider/use-block-sync'; import { store as blockEditorStore } from '../../store'; import useBlockDropZone from '../use-block-drop-zone'; -import { useSettings } from '../use-settings'; +import { unlock } from '../../lock-unlock'; const EMPTY_OBJECT = {}; @@ -72,6 +72,7 @@ function UncontrolledInnerBlocks( props ) { name, blockType, parentLock, + defaultLayout, } = props; useNestedSettingsUpdate( @@ -102,9 +103,6 @@ function UncontrolledInnerBlocks( props ) { EMPTY_OBJECT; const { allowSizingOnChildren = false } = defaultLayoutBlockSupport; - - const [ defaultLayout ] = useSettings( 'layout' ); - const usedLayout = layout || defaultLayoutBlockSupport; const memoedLayout = useMemo( @@ -186,15 +184,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) { layout = null, __unstableLayoutClassNames: layoutClassNames = '', } = useBlockEditContext(); - const { - __experimentalCaptureToolbars, - hasOverlay, - name, - blockType, - parentLock, - parentClientId, - isDropZoneDisabled, - } = useSelect( + const selected = useSelect( ( select ) => { if ( ! clientId ) { return {}; @@ -210,13 +200,15 @@ export function useInnerBlocksProps( props = {}, options = {} ) { __unstableIsWithinBlockOverlay, __unstableHasActiveBlockOverlayActive, getBlockEditingMode, - } = select( blockEditorStore ); + getBlockSettings, + } = unlock( select( blockEditorStore ) ); const { hasBlockSupport, getBlockType } = select( blocksStore ); const blockName = getBlockName( clientId ); const enableClickThrough = __unstableGetEditorMode() === 'navigation'; const blockEditingMode = getBlockEditingMode( clientId ); - const _parentClientId = getBlockRootClientId( clientId ); + const parentClientId = getBlockRootClientId( clientId ); + const [ defaultLayout ] = getBlockSettings( clientId, 'layout' ); return { __experimentalCaptureToolbars: hasBlockSupport( blockName, @@ -230,16 +222,27 @@ export function useInnerBlocksProps( props = {}, options = {} ) { enableClickThrough, name: blockName, blockType: getBlockType( blockName ), - parentLock: getTemplateLock( _parentClientId ), - parentClientId: _parentClientId, + parentLock: getTemplateLock( parentClientId ), + parentClientId, isDropZoneDisabled: blockEditingMode !== 'default' || __unstableHasActiveBlockOverlayActive( clientId ) || __unstableIsWithinBlockOverlay( clientId ), + defaultLayout, }; }, [ clientId ] ); + const { + __experimentalCaptureToolbars, + hasOverlay, + name, + blockType, + parentLock, + parentClientId, + isDropZoneDisabled, + defaultLayout, + } = selected; const blockDropZoneRef = useBlockDropZone( { dropZoneElement, @@ -259,6 +262,7 @@ export function useInnerBlocksProps( props = {}, options = {} ) { name, blockType, parentLock, + defaultLayout, ...options, }; const InnerBlocks = diff --git a/packages/block-editor/src/components/use-settings/index.js b/packages/block-editor/src/components/use-settings/index.js index 6f791a42bf1fe3..b0410b404d5e98 100644 --- a/packages/block-editor/src/components/use-settings/index.js +++ b/packages/block-editor/src/components/use-settings/index.js @@ -1,133 +1,15 @@ /** * WordPress dependencies */ -import { - __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE, - hasBlockSupport, -} from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; import deprecated from '@wordpress/deprecated'; -import { useMemo } from '@wordpress/element'; -import { applyFilters } from '@wordpress/hooks'; /** * Internal dependencies */ import { useBlockEditContext } from '../block-edit'; import { store as blockEditorStore } from '../../store'; -import { getValueFromObjectPath } from '../../utils/object'; - -const blockedPaths = [ - 'color', - 'border', - 'dimensions', - 'typography', - 'spacing', -]; - -const deprecatedFlags = { - 'color.palette': ( settings ) => settings.colors, - 'color.gradients': ( settings ) => settings.gradients, - 'color.custom': ( settings ) => - settings.disableCustomColors === undefined - ? undefined - : ! settings.disableCustomColors, - 'color.customGradient': ( settings ) => - settings.disableCustomGradients === undefined - ? undefined - : ! settings.disableCustomGradients, - 'typography.fontSizes': ( settings ) => settings.fontSizes, - 'typography.customFontSize': ( settings ) => - settings.disableCustomFontSizes === undefined - ? undefined - : ! settings.disableCustomFontSizes, - 'typography.lineHeight': ( settings ) => settings.enableCustomLineHeight, - 'spacing.units': ( settings ) => { - if ( settings.enableCustomUnits === undefined ) { - return; - } - - if ( settings.enableCustomUnits === true ) { - return [ 'px', 'em', 'rem', 'vh', 'vw', '%' ]; - } - - return settings.enableCustomUnits; - }, - 'spacing.padding': ( settings ) => settings.enableCustomSpacing, -}; - -const prefixedFlags = { - /* - * These were only available in the plugin - * and can be removed when the minimum WordPress version - * for the plugin is 5.9. - */ - 'border.customColor': 'border.color', - 'border.customStyle': 'border.style', - 'border.customWidth': 'border.width', - 'typography.customFontStyle': 'typography.fontStyle', - 'typography.customFontWeight': 'typography.fontWeight', - 'typography.customLetterSpacing': 'typography.letterSpacing', - 'typography.customTextDecorations': 'typography.textDecoration', - 'typography.customTextTransforms': 'typography.textTransform', - /* - * These were part of WordPress 5.8 and we need to keep them. - */ - 'border.customRadius': 'border.radius', - 'spacing.customMargin': 'spacing.margin', - 'spacing.customPadding': 'spacing.padding', - 'typography.customLineHeight': 'typography.lineHeight', -}; - -/** - * Remove `custom` prefixes for flags that did not land in 5.8. - * - * This provides continued support for `custom` prefixed properties. It will - * be removed once third party devs have had sufficient time to update themes, - * plugins, etc. - * - * @see https://github.com/WordPress/gutenberg/pull/34485 - * - * @param {string} path Path to desired value in settings. - * @return {string} The value for defined setting. - */ -const removeCustomPrefixes = ( path ) => { - return prefixedFlags[ path ] || path; -}; - -/** - * For settings like `color.palette`, which have a value that is an object - * with `default`, `theme`, `custom`, with field values that are arrays of - * items, merge these three arrays into one and return it. The calculation - * is memoized so that identical input values produce identical output. - * @param {Object} value Object to merge - * @return {Array} Array of merged items - */ -export function mergeOrigins( value ) { - let result = mergeCache.get( value ); - if ( ! result ) { - result = [ 'default', 'theme', 'custom' ].flatMap( - ( key ) => value[ key ] ?? [] - ); - mergeCache.set( value, result ); - } - return result; -} -const mergeCache = new WeakMap(); - -/** - * For settings like `color.palette`, which have a value that is an object - * with `default`, `theme`, `custom`, with field values that are arrays of - * items, see if any of the three origins have values. - * - * @param {Object} value Object to check - * @return {boolean} Whether the object has values in any of the three origins - */ -export function hasMergedOrigins( value ) { - return [ 'default', 'theme', 'custom' ].some( - ( key ) => value?.[ key ]?.length - ); -} +import { unlock } from '../../lock-unlock'; /** * Hook that retrieves the given settings for the block instance in use. @@ -143,120 +25,15 @@ export function hasMergedOrigins( value ) { * ``` */ export function useSettings( ...paths ) { - const { name: blockName, clientId = null } = useBlockEditContext(); - - // eslint-disable-next-line react-hooks/exhaustive-deps - paths = useMemo( () => paths, paths ); - + const { clientId = null } = useBlockEditContext(); return useSelect( - ( select ) => { - const candidates = clientId - ? [ - clientId, - ...select( blockEditorStore ).getBlockParents( - clientId, - /* ascending */ true - ), - ].filter( ( candidateClientId ) => { - const candidateBlockName = - select( blockEditorStore ).getBlockName( - candidateClientId - ); - return hasBlockSupport( - candidateBlockName, - '__experimentalSettings', - false - ); - } ) - : []; - - return paths.map( ( path ) => { - if ( blockedPaths.includes( path ) ) { - // eslint-disable-next-line no-console - console.warn( - 'Top level useSetting paths are disabled. Please use a subpath to query the information needed.' - ); - return undefined; - } - - // 0. Allow third parties to filter the block's settings at runtime. - let result = applyFilters( - 'blockEditor.useSetting.before', - undefined, - path, - clientId, - blockName - ); - - if ( undefined !== result ) { - return result; - } - - const normalizedPath = removeCustomPrefixes( path ); - - // 1. Take settings from the block instance or its ancestors. - // Start from the current block and work our way up the ancestors. - for ( const candidateClientId of candidates ) { - const candidateAtts = - select( blockEditorStore ).getBlockAttributes( - candidateClientId - ); - result = - getValueFromObjectPath( - candidateAtts.settings?.blocks?.[ blockName ], - normalizedPath - ) ?? - getValueFromObjectPath( - candidateAtts.settings, - normalizedPath - ); - if ( result !== undefined ) { - // Stop the search for more distant ancestors and move on. - break; - } - } - - // 2. Fall back to the settings from the block editor store (__experimentalFeatures). - const settings = select( blockEditorStore ).getSettings(); - if ( result === undefined && blockName ) { - result = getValueFromObjectPath( - settings.__experimentalFeatures?.blocks?.[ blockName ], - normalizedPath - ); - } - - if ( result === undefined ) { - result = getValueFromObjectPath( - settings.__experimentalFeatures, - normalizedPath - ); - } - - // Return if the setting was found in either the block instance or the store. - if ( result !== undefined ) { - if ( PATHS_WITH_MERGE[ normalizedPath ] ) { - return mergeOrigins( result ); - } - return result; - } - - // 3. Otherwise, use deprecated settings. - const deprecatedSettingsValue = - deprecatedFlags[ normalizedPath ]?.( settings ); - if ( deprecatedSettingsValue !== undefined ) { - return deprecatedSettingsValue; - } - - // 4. Fallback for typography.dropCap: - // This is only necessary to support typography.dropCap. - // when __experimentalFeatures are not present (core without plugin). - // To remove when __experimentalFeatures are ported to core. - return normalizedPath === 'typography.dropCap' - ? true - : undefined; - } ); - }, - [ blockName, clientId, paths ] + ( select ) => + unlock( select( blockEditorStore ) ).getBlockSettings( + clientId, + ...paths + ), + // eslint-disable-next-line react-hooks/exhaustive-deps + [ clientId, ...paths ] ); } diff --git a/packages/block-editor/src/components/use-settings/test/index.js b/packages/block-editor/src/components/use-settings/test/index.js deleted file mode 100644 index c9174cf35df5f7..00000000000000 --- a/packages/block-editor/src/components/use-settings/test/index.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * External dependencies - */ -import { render } from '@testing-library/react'; - -/** - * WordPress dependencies - */ -import { addFilter, removeFilter } from '@wordpress/hooks'; -import { useSelect } from '@wordpress/data'; -import { useEffect } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { useSettings, useSetting } from '..'; -import * as BlockEditContext from '../../block-edit/context'; - -// Mock useSelect() functions used by useSettings() -jest.mock( '@wordpress/data/src/components/use-select' ); - -let selectMock = {}; -const setupSelectMock = () => { - selectMock = { - getSettings: () => ( {} ), - getBlockParents: () => [], - getBlockName: () => '', - }; -}; - -useSelect.mockImplementation( ( callback ) => callback( () => selectMock ) ); - -const mockSettings = ( settings ) => { - selectMock.getSettings = () => ( { - __experimentalFeatures: settings, - } ); -}; - -const mockCurrentBlockContext = ( - blockContext = { name: '', isSelected: false } -) => { - jest.spyOn( BlockEditContext, 'useBlockEditContext' ).mockReturnValue( - blockContext - ); -}; - -function runHook( hookCb ) { - let storedResult; - function TestHook() { - const result = hookCb(); - useEffect( () => { - storedResult = result; - }, [ result ] ); - } - render( ); - return storedResult; -} - -describe( 'useSettings', () => { - beforeEach( () => { - setupSelectMock(); - mockCurrentBlockContext(); - } ); - - it( 'uses block setting', () => { - mockSettings( { - blocks: { - 'core/test-block': { - layout: { - contentSize: '840px', - }, - }, - }, - } ); - - mockCurrentBlockContext( { - name: 'core/test-block', - } ); - - const result = runHook( () => useSettings( 'layout.contentSize' ) ); - expect( result ).toEqual( [ '840px' ] ); - } ); - - it( 'uses blockEditor.useSetting.before hook override', () => { - mockSettings( { - blocks: { - 'core/test-block': { - layout: { - contentSize: '840px', - }, - }, - }, - } ); - - mockCurrentBlockContext( { - name: 'core/test-block', - } ); - - addFilter( - 'blockEditor.useSetting.before', - 'test/useSetting.before', - ( result, path, clientId, blockName ) => { - if ( blockName === 'core/test-block' ) { - return '960px'; - } - - return result; - } - ); - - const result = runHook( () => useSettings( 'layout.contentSize' ) ); - expect( result ).toEqual( [ '960px' ] ); - - removeFilter( - 'blockEditor.useSetting.before', - 'test/useSetting.before' - ); - } ); - - it( 'supports also the deprecated useSetting function', () => { - mockSettings( { - blocks: { - 'core/test-block': { - layout: { - contentSize: '840px', - }, - }, - }, - } ); - - mockCurrentBlockContext( { - name: 'core/test-block', - } ); - - const result = runHook( () => useSetting( 'layout.contentSize' ) ); - expect( result ).toBe( '840px' ); - expect( console ).toHaveWarnedWith( - 'wp.blockEditor.useSetting is deprecated since version 6.5. Please use wp.blockEditor.useSettings instead.' - ); - } ); -} ); diff --git a/packages/block-editor/src/store/get-block-settings.js b/packages/block-editor/src/store/get-block-settings.js new file mode 100644 index 00000000000000..819ad51f4b0e9f --- /dev/null +++ b/packages/block-editor/src/store/get-block-settings.js @@ -0,0 +1,235 @@ +/** + * WordPress dependencies + */ +import { + __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE, + hasBlockSupport, +} from '@wordpress/blocks'; +import { applyFilters } from '@wordpress/hooks'; + +/** + * Internal dependencies + */ +import { getValueFromObjectPath } from '../utils/object'; +import { + getBlockParents, + getBlockName, + getSettings, + getBlockAttributes, +} from './selectors'; + +const blockedPaths = [ + 'color', + 'border', + 'dimensions', + 'typography', + 'spacing', +]; + +const deprecatedFlags = { + 'color.palette': ( settings ) => settings.colors, + 'color.gradients': ( settings ) => settings.gradients, + 'color.custom': ( settings ) => + settings.disableCustomColors === undefined + ? undefined + : ! settings.disableCustomColors, + 'color.customGradient': ( settings ) => + settings.disableCustomGradients === undefined + ? undefined + : ! settings.disableCustomGradients, + 'typography.fontSizes': ( settings ) => settings.fontSizes, + 'typography.customFontSize': ( settings ) => + settings.disableCustomFontSizes === undefined + ? undefined + : ! settings.disableCustomFontSizes, + 'typography.lineHeight': ( settings ) => settings.enableCustomLineHeight, + 'spacing.units': ( settings ) => { + if ( settings.enableCustomUnits === undefined ) { + return; + } + + if ( settings.enableCustomUnits === true ) { + return [ 'px', 'em', 'rem', 'vh', 'vw', '%' ]; + } + + return settings.enableCustomUnits; + }, + 'spacing.padding': ( settings ) => settings.enableCustomSpacing, +}; + +const prefixedFlags = { + /* + * These were only available in the plugin + * and can be removed when the minimum WordPress version + * for the plugin is 5.9. + */ + 'border.customColor': 'border.color', + 'border.customStyle': 'border.style', + 'border.customWidth': 'border.width', + 'typography.customFontStyle': 'typography.fontStyle', + 'typography.customFontWeight': 'typography.fontWeight', + 'typography.customLetterSpacing': 'typography.letterSpacing', + 'typography.customTextDecorations': 'typography.textDecoration', + 'typography.customTextTransforms': 'typography.textTransform', + /* + * These were part of WordPress 5.8 and we need to keep them. + */ + 'border.customRadius': 'border.radius', + 'spacing.customMargin': 'spacing.margin', + 'spacing.customPadding': 'spacing.padding', + 'typography.customLineHeight': 'typography.lineHeight', +}; + +/** + * Remove `custom` prefixes for flags that did not land in 5.8. + * + * This provides continued support for `custom` prefixed properties. It will + * be removed once third party devs have had sufficient time to update themes, + * plugins, etc. + * + * @see https://github.com/WordPress/gutenberg/pull/34485 + * + * @param {string} path Path to desired value in settings. + * @return {string} The value for defined setting. + */ +const removeCustomPrefixes = ( path ) => { + return prefixedFlags[ path ] || path; +}; + +/** + * For settings like `color.palette`, which have a value that is an object + * with `default`, `theme`, `custom`, with field values that are arrays of + * items, merge these three arrays into one and return it. The calculation + * is memoized so that identical input values produce identical output. + * @param {Object} value Object to merge + * @return {Array} Array of merged items + */ +export function mergeOrigins( value ) { + let result = mergeCache.get( value ); + if ( ! result ) { + result = [ 'default', 'theme', 'custom' ].flatMap( + ( key ) => value[ key ] ?? [] + ); + mergeCache.set( value, result ); + } + return result; +} +const mergeCache = new WeakMap(); + +/** + * For settings like `color.palette`, which have a value that is an object + * with `default`, `theme`, `custom`, with field values that are arrays of + * items, see if any of the three origins have values. + * + * @param {Object} value Object to check + * @return {boolean} Whether the object has values in any of the three origins + */ +export function hasMergedOrigins( value ) { + return [ 'default', 'theme', 'custom' ].some( + ( key ) => value?.[ key ]?.length + ); +} + +export function getBlockSettings( state, clientId, ...paths ) { + const blockName = getBlockName( state, clientId ); + const candidates = clientId + ? [ + clientId, + ...getBlockParents( state, clientId, /* ascending */ true ), + ].filter( ( candidateClientId ) => { + const candidateBlockName = getBlockName( + state, + candidateClientId + ); + return hasBlockSupport( + candidateBlockName, + '__experimentalSettings', + false + ); + } ) + : []; + + return paths.map( ( path ) => { + if ( blockedPaths.includes( path ) ) { + // eslint-disable-next-line no-console + console.warn( + 'Top level useSetting paths are disabled. Please use a subpath to query the information needed.' + ); + return undefined; + } + + // 0. Allow third parties to filter the block's settings at runtime. + let result = applyFilters( + 'blockEditor.useSetting.before', + undefined, + path, + clientId, + blockName + ); + + if ( undefined !== result ) { + return result; + } + + const normalizedPath = removeCustomPrefixes( path ); + + // 1. Take settings from the block instance or its ancestors. + // Start from the current block and work our way up the ancestors. + for ( const candidateClientId of candidates ) { + const candidateAtts = getBlockAttributes( + state, + candidateClientId + ); + result = + getValueFromObjectPath( + candidateAtts.settings?.blocks?.[ blockName ], + normalizedPath + ) ?? + getValueFromObjectPath( + candidateAtts.settings, + normalizedPath + ); + if ( result !== undefined ) { + // Stop the search for more distant ancestors and move on. + break; + } + } + + // 2. Fall back to the settings from the block editor store (__experimentalFeatures). + const settings = getSettings( state ); + if ( result === undefined && blockName ) { + result = getValueFromObjectPath( + settings.__experimentalFeatures?.blocks?.[ blockName ], + normalizedPath + ); + } + + if ( result === undefined ) { + result = getValueFromObjectPath( + settings.__experimentalFeatures, + normalizedPath + ); + } + + // Return if the setting was found in either the block instance or the store. + if ( result !== undefined ) { + if ( PATHS_WITH_MERGE[ normalizedPath ] ) { + return mergeOrigins( result ); + } + return result; + } + + // 3. Otherwise, use deprecated settings. + const deprecatedSettingsValue = + deprecatedFlags[ normalizedPath ]?.( settings ); + if ( deprecatedSettingsValue !== undefined ) { + return deprecatedSettingsValue; + } + + // 4. Fallback for typography.dropCap: + // This is only necessary to support typography.dropCap. + // when __experimentalFeatures are not present (core without plugin). + // To remove when __experimentalFeatures are ported to core. + return normalizedPath === 'typography.dropCap' ? true : undefined; + } ); +} diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index da32272c8ff232..77374abcb62e4d 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -20,9 +20,11 @@ import { } from './selectors'; import { checkAllowListRecursive, getAllPatternsDependants } from './utils'; import { INSERTER_PATTERN_TYPES } from '../components/inserter/block-patterns-tab/utils'; -import { store } from './'; +import { STORE_NAME } from './constants'; import { unlock } from '../lock-unlock'; +export { getBlockSettings } from './get-block-settings'; + /** * Returns true if the block interface is hidden, or false otherwise. * @@ -262,7 +264,7 @@ export const hasAllowedPatterns = createRegistrySelector( ( select ) => createSelector( ( state, rootClientId = null ) => { const { getAllPatterns, __experimentalGetParsedPattern } = unlock( - select( store ) + select( STORE_NAME ) ); const patterns = getAllPatterns(); const { allowedBlockTypes } = getSettings( state ); @@ -320,7 +322,7 @@ export const getAllPatterns = createRegistrySelector( ( select ) => return [ ...userPatterns, ...__experimentalBlockPatterns, - ...unlock( select( store ) ).getFetchedPatterns(), + ...unlock( select( STORE_NAME ) ).getFetchedPatterns(), ].filter( ( x, index, arr ) => index === arr.findIndex( ( y ) => x.name === y.name ) diff --git a/packages/block-editor/src/store/test/get-block-settings.js b/packages/block-editor/src/store/test/get-block-settings.js new file mode 100644 index 00000000000000..7154b3edc1d73b --- /dev/null +++ b/packages/block-editor/src/store/test/get-block-settings.js @@ -0,0 +1,90 @@ +/** + * WordPress dependencies + */ +import { addFilter, removeFilter } from '@wordpress/hooks'; + +/** + * Internal dependencies + */ +import { getBlockSettings } from '../get-block-settings'; + +describe( 'getBlockSettings', () => { + it( 'uses block setting', () => { + const state = { + settings: { + __experimentalFeatures: { + blocks: { + 'core/test-block': { + layout: { + contentSize: '840px', + }, + }, + }, + }, + }, + blocks: { + byClientId: new Map( [ + [ + 'block-1', + { + name: 'core/test-block', + }, + ], + ] ), + parents: new Map( [ [ 'block-1', '' ] ] ), + }, + }; + + expect( + getBlockSettings( state, 'block-1', 'layout.contentSize' ) + ).toEqual( [ '840px' ] ); + } ); + + it( 'uses blockEditor.useSetting.before hook override', () => { + const state = { + settings: { + __experimentalFeatures: { + blocks: { + 'core/test-block': { + layout: { + contentSize: '840px', + }, + }, + }, + }, + }, + blocks: { + byClientId: new Map( [ + [ + 'block-1', + { + name: 'core/test-block', + }, + ], + ] ), + parents: new Map( [ [ 'block-1', '' ] ] ), + }, + }; + + addFilter( + 'blockEditor.useSetting.before', + 'test/useSetting.before', + ( result, path, clientId, blockName ) => { + if ( blockName === 'core/test-block' ) { + return '960px'; + } + + return result; + } + ); + + expect( + getBlockSettings( state, 'block-1', 'layout.contentSize' ) + ).toEqual( [ '960px' ] ); + + removeFilter( + 'blockEditor.useSetting.before', + 'test/useSetting.before' + ); + } ); +} ); From 8c2c0938d71e97ec3350f5e357f7beb92ed348a0 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Mon, 29 Jan 2024 11:16:24 -0600 Subject: [PATCH 15/45] Font Library: addresses additional REST API feedback (#58333) --- .../class-wp-rest-font-faces-controller.php | 15 ++++++++++++++- .../class-wp-rest-font-families-controller.php | 15 ++++++++++++++- .../fonts/font-library/font-library.php | 11 ++--------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php index 68173dd43f47dc..f35204517ab87c 100644 --- a/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php +++ b/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php @@ -130,7 +130,20 @@ public function get_items_permissions_check( $request ) { // phpcs:ignore Variab * @return true|WP_Error True if the request has read access, WP_Error object otherwise. */ public function get_item_permissions_check( $request ) { - return $this->get_items_permissions_check( $request ); + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; + } + + if ( ! current_user_can( 'read_post', $post->ID ) ) { + return new WP_Error( + 'rest_cannot_read', + __( 'Sorry, you are not allowed to access this font face.', 'gutenberg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + return true; } /** diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php index 4d5a89f6de1a05..d00f1bd9d24a94 100644 --- a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php +++ b/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php @@ -56,7 +56,20 @@ public function get_items_permissions_check( $request ) { // phpcs:ignore Variab * @return true|WP_Error True if the request has read access, WP_Error object otherwise. */ public function get_item_permissions_check( $request ) { - return $this->get_items_permissions_check( $request ); + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; + } + + if ( ! current_user_can( 'read_post', $post->ID ) ) { + return new WP_Error( + 'rest_cannot_read', + __( 'Sorry, you are not allowed to access this font family.', 'gutenberg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + return true; } /** diff --git a/lib/experimental/fonts/font-library/font-library.php b/lib/experimental/fonts/font-library/font-library.php index 0bf9374f9f9454..59c2552e54c945 100644 --- a/lib/experimental/fonts/font-library/font-library.php +++ b/lib/experimental/fonts/font-library/font-library.php @@ -31,20 +31,17 @@ function gutenberg_init_font_library_routes() { 'hierarchical' => false, 'capabilities' => array( 'read' => 'edit_theme_options', - 'read_post' => 'edit_theme_options', 'read_private_posts' => 'edit_theme_options', 'create_posts' => 'edit_theme_options', 'publish_posts' => 'edit_theme_options', - 'edit_post' => 'edit_theme_options', 'edit_posts' => 'edit_theme_options', 'edit_others_posts' => 'edit_theme_options', 'edit_published_posts' => 'edit_theme_options', - 'delete_post' => 'edit_theme_options', 'delete_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', 'delete_published_posts' => 'edit_theme_options', ), - 'map_meta_cap' => false, + 'map_meta_cap' => true, 'query_var' => false, 'show_in_rest' => true, 'rest_base' => 'font-families', @@ -66,20 +63,17 @@ function gutenberg_init_font_library_routes() { 'hierarchical' => false, 'capabilities' => array( 'read' => 'edit_theme_options', - 'read_post' => 'edit_theme_options', 'read_private_posts' => 'edit_theme_options', 'create_posts' => 'edit_theme_options', 'publish_posts' => 'edit_theme_options', - 'edit_post' => 'edit_theme_options', 'edit_posts' => 'edit_theme_options', 'edit_others_posts' => 'edit_theme_options', 'edit_published_posts' => 'edit_theme_options', - 'delete_post' => 'edit_theme_options', 'delete_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', 'delete_published_posts' => 'edit_theme_options', ), - 'map_meta_cap' => false, + 'map_meta_cap' => true, 'query_var' => false, 'show_in_rest' => true, 'rest_base' => 'font-families/(?P[\d]+)/font-faces', @@ -263,7 +257,6 @@ function gutenberg_convert_legacy_font_family_format() { 'post_type' => 'wp_font_family', // Set a maximum, but in reality there will be far less than this. 'posts_per_page' => 999, - 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) ); From 94fbb4e57a74b17a7f6dee08713e5899f0534a51 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Mon, 29 Jan 2024 11:18:31 -0600 Subject: [PATCH 16/45] Font Library: remove unused utilities and rename class (#58342) --- ...mily-utils.php => class-wp-font-utils.php} | 68 +--- .../class-wp-rest-font-faces-controller.php | 6 +- ...class-wp-rest-font-families-controller.php | 2 +- .../fonts/font-library/font-library.php | 2 +- lib/load.php | 2 +- .../getFilenameFromFontFace.php | 64 ---- .../wpFontFamilyUtils/mergeFontsData.php | 300 ------------------ .../wpFontLibrary/getMimeTypes.php | 6 +- .../formatFontFamily.php | 8 +- .../getFontFaceSlug.php | 8 +- .../wpRestFontFacesController.php | 2 +- 11 files changed, 26 insertions(+), 442 deletions(-) rename lib/experimental/fonts/font-library/{class-wp-font-family-utils.php => class-wp-font-utils.php} (64%) delete mode 100644 phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFilenameFromFontFace.php delete mode 100644 phpunit/tests/fonts/font-library/wpFontFamilyUtils/mergeFontsData.php rename phpunit/tests/fonts/font-library/{wpFontFamilyUtils => wpFontUtils}/formatFontFamily.php (84%) rename phpunit/tests/fonts/font-library/{wpFontFamilyUtils => wpFontUtils}/getFontFaceSlug.php (90%) diff --git a/lib/experimental/fonts/font-library/class-wp-font-family-utils.php b/lib/experimental/fonts/font-library/class-wp-font-utils.php similarity index 64% rename from lib/experimental/fonts/font-library/class-wp-font-family-utils.php rename to lib/experimental/fonts/font-library/class-wp-font-utils.php index b291a8f1ee348d..b75f70bd80de87 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-family-utils.php +++ b/lib/experimental/fonts/font-library/class-wp-font-utils.php @@ -9,77 +9,24 @@ * @since 6.5.0 */ -if ( class_exists( 'WP_Font_Family_Utils' ) ) { +if ( class_exists( 'WP_Font_Utils' ) ) { return; } /** * A class of utilities for working with the Font Library. * + * These utilities may change or be removed in the future and are intended for internal use only. + * * @since 6.5.0 + * @access private */ -class WP_Font_Family_Utils { - - /** - * Generates a filename for a font face asset. - * - * Creates a filename for a font face asset using font family, style, weight and - * extension information. - * - * @since 6.5.0 - * - * @param string $font_slug The font slug to use in the filename. - * @param array $font_face The font face array containing 'fontFamily', 'fontStyle', and - * 'fontWeight' attributes. - * @param string $url The URL of the font face asset, used to derive the file extension. - * @param string $suffix Optional. The suffix added to the resulting filename. Default empty string. - * @return string The generated filename for the font face asset. - */ - public static function get_filename_from_font_face( $font_slug, $font_face, $url, $suffix = '' ) { - $extension = pathinfo( $url, PATHINFO_EXTENSION ); - $filename = "{$font_slug}_{$font_face['fontStyle']}_{$font_face['fontWeight']}"; - if ( '' !== $suffix ) { - $filename .= "_{$suffix}"; - } - - return sanitize_file_name( "{$filename}.{$extension}" ); - } - - /** - * Merges two fonts and their font faces. - * - * @since 6.5.0 - * - * @param array $font1 The first font to merge. - * @param array $font2 The second font to merge. - * @return array|WP_Error The merged font or WP_Error if the fonts have different slugs. - */ - public static function merge_fonts_data( $font1, $font2 ) { - if ( $font1['slug'] !== $font2['slug'] ) { - return new WP_Error( - 'fonts_must_have_same_slug', - __( 'Fonts must have the same slug to be merged.', 'gutenberg' ) - ); - } - - $font_faces_1 = isset( $font1['fontFace'] ) ? $font1['fontFace'] : array(); - $font_faces_2 = isset( $font2['fontFace'] ) ? $font2['fontFace'] : array(); - $merged_font_faces = array_merge( $font_faces_1, $font_faces_2 ); - - $serialized_faces = array_map( 'serialize', $merged_font_faces ); - $unique_serialized_faces = array_unique( $serialized_faces ); - $unique_faces = array_map( 'unserialize', $unique_serialized_faces ); - - $merged_font = array_merge( $font1, $font2 ); - $merged_font['fontFace'] = array_values( $unique_faces ); - - return $merged_font; - } - +class WP_Font_Utils { /** - * Format font family to make it valid CSS. + * Format font family names with surrounding quotes when the name contains a space. * * @since 6.5.0 + * @access private * * @param string $font_family Font family attribute. * @return string The formatted font family attribute. @@ -117,6 +64,7 @@ function ( $family ) { * unicode ranges. * * @since 6.5.0 + * @access private * * @link https://drafts.csswg.org/css-fonts/#font-style-matching * diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php index f35204517ab87c..c727e35419e933 100644 --- a/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php +++ b/lib/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php @@ -231,7 +231,7 @@ public function sanitize_font_face_settings( $value ) { $settings = json_decode( $value, true ); if ( isset( $settings['fontFamily'] ) ) { - $settings['fontFamily'] = WP_Font_Family_Utils::format_font_family( $settings['fontFamily'] ); + $settings['fontFamily'] = WP_Font_Utils::format_font_family( $settings['fontFamily'] ); } return $settings; @@ -309,7 +309,7 @@ public function create_item( $request ) { array( 'post_type' => $this->post_type, 'posts_per_page' => 1, - 'title' => WP_Font_Family_Utils::get_font_face_slug( $settings ), + 'title' => WP_Font_Utils::get_font_face_slug( $settings ), 'update_post_meta_cache' => false, 'update_post_term_cache' => false, ) @@ -731,7 +731,7 @@ protected function prepare_item_for_database( $request ) { // Store this "slug" as the post_title rather than post_name, since it uses the fontFamily setting, // which may contain multibyte characters. - $title = WP_Font_Family_Utils::get_font_face_slug( $settings ); + $title = WP_Font_Utils::get_font_face_slug( $settings ); $prepared_post->post_type = $this->post_type; $prepared_post->post_parent = $request['font_family_id']; diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php index d00f1bd9d24a94..90d0e075bdefb6 100644 --- a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php +++ b/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php @@ -146,7 +146,7 @@ public function sanitize_font_family_settings( $value ) { $settings = json_decode( $value, true ); if ( isset( $settings['fontFamily'] ) ) { - $settings['fontFamily'] = WP_Font_Family_Utils::format_font_family( $settings['fontFamily'] ); + $settings['fontFamily'] = WP_Font_Utils::format_font_family( $settings['fontFamily'] ); } // Provide default for preview, if not provided. diff --git a/lib/experimental/fonts/font-library/font-library.php b/lib/experimental/fonts/font-library/font-library.php index 59c2552e54c945..5af21f11ebbb26 100644 --- a/lib/experimental/fonts/font-library/font-library.php +++ b/lib/experimental/fonts/font-library/font-library.php @@ -282,7 +282,7 @@ function gutenberg_convert_legacy_font_family_format() { foreach ( $font_faces as $font_face ) { $args = array(); $args['post_type'] = 'wp_font_face'; - $args['post_title'] = WP_Font_Family_Utils::get_font_face_slug( $font_face ); + $args['post_title'] = WP_Font_Utils::get_font_face_slug( $font_face ); $args['post_name'] = sanitize_title( $args['post_title'] ); $args['post_status'] = 'publish'; $args['post_parent'] = $font_family->ID; diff --git a/lib/load.php b/lib/load.php index 4b2b4d5d8b0db8..6ded190b04e3fb 100644 --- a/lib/load.php +++ b/lib/load.php @@ -137,7 +137,7 @@ function gutenberg_is_experiment_enabled( $name ) { // Loads the Font Library. require __DIR__ . '/experimental/fonts/font-library/class-wp-font-collection.php'; require __DIR__ . '/experimental/fonts/font-library/class-wp-font-library.php'; -require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family-utils.php'; +require __DIR__ . '/experimental/fonts/font-library/class-wp-font-utils.php'; require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-families-controller.php'; require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php'; require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php'; diff --git a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFilenameFromFontFace.php b/phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFilenameFromFontFace.php deleted file mode 100644 index 0bd5b47ea23785..00000000000000 --- a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFilenameFromFontFace.php +++ /dev/null @@ -1,64 +0,0 @@ -assertSame( - $expected, - WP_Font_Family_Utils::get_filename_from_font_face( - $slug, - $font_face, - $font_face['src'], - $suffix - ) - ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_should_get_filename() { - return array( - 'piazzolla' => array( - 'slug' => 'piazzolla', - 'font_face' => array( - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/font_file.ttf', - ), - 'suffix' => '', - 'expected_file_name' => 'piazzolla_italic_400.ttf', - ), - 'inter' => array( - 'slug' => 'inter', - 'font_face' => array( - 'fontStyle' => 'normal', - 'fontWeight' => '600', - 'src' => 'http://example.com/fonts/font_file.otf', - ), - 'suffix' => '', - 'expected_file_name' => 'inter_normal_600.otf', - ), - ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/mergeFontsData.php b/phpunit/tests/fonts/font-library/wpFontFamilyUtils/mergeFontsData.php deleted file mode 100644 index 21517a0970f932..00000000000000 --- a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/mergeFontsData.php +++ /dev/null @@ -1,300 +0,0 @@ -assertWPError( $actual, 'WP_Error should have been returned' ); - $this->assertSame( - array( 'fonts_must_have_same_slug' => array( 'Fonts must have the same slug to be merged.' ) ), - $actual->errors, - 'WP_Error should have "fonts_must_have_same_slug" error' - ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_should_fail_merge() { - return array( - 'different slugs' => array( - 'font1' => array( - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - ), - ), - 'font2' => array( - 'slug' => 'inter', - 'fontFamily' => 'Inter', - 'fontFace' => array( - array( - 'fontFamily' => 'Inter', - 'fontStyle' => 'normal', - 'fontWeight' => '700', - 'src' => 'http://example.com/fonts/inter_700_normal.ttf', - ), - ), - ), - 'expected_result' => 'WP_Error', - ), - ); - } - - - /** - * @dataProvider data_should_merge - * - * @param array $font1 First font data in theme.json format. - * @param array $font2 Second font data in theme.json format. - * @param array $expected_result Expected result. - */ - public function test_should_merge( array $font1, array $font2, array $expected_result ) { - $result = WP_Font_Family_Utils::merge_fonts_data( $font1, $font2 ); - $this->assertSame( $expected_result, $result, 'Merged font data should match expected result.' ); - $json_result = wp_json_encode( $result ); - $this->assertStringContainsString( '"fontFace":[', $json_result, 'fontFace data should be enconded as an array and not an object.' ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_should_merge() { - return array( - 'with different font faces' => array( - 'font1' => array( - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '500', - 'src' => 'http://example.com/fonts/piazzolla_500_italic.ttf', - ), - ), - ), - 'font2' => array( - 'slug' => 'piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '600', - 'src' => 'http://example.com/fonts/piazzolla_600_normal.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '700', - 'src' => 'http://example.com/fonts/piazzolla_700_normal.ttf', - ), - ), - ), - 'expected_result' => array( - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '500', - 'src' => 'http://example.com/fonts/piazzolla_500_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '600', - 'src' => 'http://example.com/fonts/piazzolla_600_normal.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '700', - 'src' => 'http://example.com/fonts/piazzolla_700_normal.ttf', - ), - ), - ), - ), - - 'repeated font faces' => array( - 'font1' => array( - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '500', - 'src' => 'http://example.com/fonts/piazzolla_500_italic.ttf', - ), - ), - ), - 'font2' => array( - 'slug' => 'piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '600', - 'src' => 'http://example.com/fonts/piazzolla_600_normal.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '500', - 'src' => 'http://example.com/fonts/piazzolla_500_italic.ttf', - ), - ), - ), - 'expected_result' => array( - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '500', - 'src' => 'http://example.com/fonts/piazzolla_500_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '600', - 'src' => 'http://example.com/fonts/piazzolla_600_normal.ttf', - ), - ), - ), - ), - 'repeated font faces with non consecutive index positions' => array( - 'font1' => array( - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - - ), - ), - 'font2' => array( - 'slug' => 'piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '600', - 'src' => 'http://example.com/fonts/piazzolla_600_normal.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '500', - 'src' => 'http://example.com/fonts/piazzolla_500_italic.ttf', - ), - ), - ), - 'expected_result' => array( - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFamily' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '400', - 'src' => 'http://example.com/fonts/piazzolla_400_italic.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '600', - 'src' => 'http://example.com/fonts/piazzolla_600_normal.ttf', - ), - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'italic', - 'fontWeight' => '500', - 'src' => 'http://example.com/fonts/piazzolla_500_italic.ttf', - ), - ), - ), - ), - ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php index 485587060f16a1..579baa2d248e0b 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php @@ -1,6 +1,6 @@ assertSame( $expected, - WP_Font_Family_Utils::format_font_family( + WP_Font_Utils::format_font_family( $font_family ) ); diff --git a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFontFaceSlug.php b/phpunit/tests/fonts/font-library/wpFontUtils/getFontFaceSlug.php similarity index 90% rename from phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFontFaceSlug.php rename to phpunit/tests/fonts/font-library/wpFontUtils/getFontFaceSlug.php index 1f87d0d2fd5a11..ded19749b9123e 100644 --- a/phpunit/tests/fonts/font-library/wpFontFamilyUtils/getFontFaceSlug.php +++ b/phpunit/tests/fonts/font-library/wpFontUtils/getFontFaceSlug.php @@ -1,18 +1,18 @@ assertSame( $expected_slug, $slug ); } diff --git a/phpunit/tests/fonts/font-library/wpRestFontFacesController.php b/phpunit/tests/fonts/font-library/wpRestFontFacesController.php index 6091a3c91dc9ec..d6a95814b205a2 100644 --- a/phpunit/tests/fonts/font-library/wpRestFontFacesController.php +++ b/phpunit/tests/fonts/font-library/wpRestFontFacesController.php @@ -69,7 +69,7 @@ public static function wpTearDownAfterClass() { public static function create_font_face_post( $parent_id, $settings = array() ) { $settings = array_merge( self::$default_settings, $settings ); - $title = WP_Font_Family_Utils::get_font_face_slug( $settings ); + $title = WP_Font_Utils::get_font_face_slug( $settings ); return self::factory()->post->create( wp_slash( array( From 0395847b9c08d1a6ff56fa92555dba4b62d30da2 Mon Sep 17 00:00:00 2001 From: Nik Tsekouras Date: Mon, 29 Jan 2024 19:46:52 +0200 Subject: [PATCH 17/45] DataViews: Fix applied default layout props (#58400) --- .../src/components/page-templates-template-parts/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/page-templates-template-parts/index.js b/packages/edit-site/src/components/page-templates-template-parts/index.js index a6c2864ad98f3e..b95489b480f13d 100644 --- a/packages/edit-site/src/components/page-templates-template-parts/index.js +++ b/packages/edit-site/src/components/page-templates-template-parts/index.js @@ -192,11 +192,13 @@ export default function PageTemplatesTemplateParts( { postType } ) { const { params } = useLocation(); const { activeView = 'all', layout } = params; const defaultView = useMemo( () => { + const usedType = window?.__experimentalAdminViews + ? layout ?? DEFAULT_VIEW.type + : DEFAULT_VIEW.type; return { ...DEFAULT_VIEW, - type: window?.__experimentalAdminViews - ? layout ?? DEFAULT_VIEW.type - : DEFAULT_VIEW.type, + type: usedType, + layout: defaultConfigPerViewType[ usedType ], filters: activeView !== 'all' ? [ From 0d5e9ee05567c7bb9c47fb18efc899b0213d712a Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Tue, 30 Jan 2024 08:54:01 +1300 Subject: [PATCH 18/45] Change text on pattern reset button (#58286) --- packages/block-library/src/block/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index 8ec7275c0e9f87..b76b655ee0d943 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -408,7 +408,7 @@ export default function ReusableBlockEdit( { disabled={ ! overrides } __experimentalIsFocusable > - { __( 'Reset to original' ) } + { __( 'Reset' ) } From 84b09ce1aea05af6b6a9d1b520e24c22dfb749d4 Mon Sep 17 00:00:00 2001 From: Artemio Morales Date: Mon, 29 Jan 2024 15:35:31 -0500 Subject: [PATCH 19/45] Image block: Move UI for lightbox from sidebar to the content toolbar alongside link settings (#57608) * Add first pass at Expand on Click in toolbar * Style changes * Fix lightbox setting check for toolbar * Remove old UI * Sync toolbar UI with global styles settings * Compact buttons * Update style.scss * Update UI * Make input width smaller * Remove chevron in Expand on Click popover * Improve markup and styles * Remove erroneous closing of popover after link is removed * Improve button label for removing lightbox * Update label name in test --------- Co-authored-by: Rich Tabor --- .../components/media-replace-flow/style.scss | 2 +- .../url-popover/image-url-input-ui.js | 123 ++++++++++++------ .../src/components/url-popover/index.js | 2 + .../src/components/url-popover/link-editor.js | 1 + .../src/components/url-popover/link-viewer.js | 1 + .../src/components/url-popover/style.scss | 82 +++++++----- packages/block-library/src/image/image.js | 53 +++----- test/e2e/specs/editor/blocks/image.spec.js | 5 +- 8 files changed, 163 insertions(+), 106 deletions(-) diff --git a/packages/block-editor/src/components/media-replace-flow/style.scss b/packages/block-editor/src/components/media-replace-flow/style.scss index 61df542cf58404..21fc489b8a2c64 100644 --- a/packages/block-editor/src/components/media-replace-flow/style.scss +++ b/packages/block-editor/src/components/media-replace-flow/style.scss @@ -27,7 +27,7 @@ } .block-editor-link-control { - width: 300px; // Hardcoded width avoids resizing of control when switching between preview/edit. + width: 200px; // Hardcoded width avoids resizing of control when switching between preview/edit. .block-editor-url-input { padding: 0; // Cancel unnecessary default 1px padding in this case. diff --git a/packages/block-editor/src/components/url-popover/image-url-input-ui.js b/packages/block-editor/src/components/url-popover/image-url-input-ui.js index 7caa218658b24c..f6946b2c8e65d2 100644 --- a/packages/block-editor/src/components/url-popover/image-url-input-ui.js +++ b/packages/block-editor/src/components/url-popover/image-url-input-ui.js @@ -6,15 +6,19 @@ import { useRef, useState } from '@wordpress/element'; import { ToolbarButton, Button, - NavigableMenu, MenuItem, ToggleControl, TextControl, - SVG, - Path, + MenuGroup, __experimentalVStack as VStack, } from '@wordpress/components'; -import { link as linkIcon, close } from '@wordpress/icons'; +import { + link as linkIcon, + image, + page, + fullscreen, + linkOff, +} from '@wordpress/icons'; /** * Internal dependencies @@ -27,14 +31,6 @@ const LINK_DESTINATION_MEDIA = 'media'; const LINK_DESTINATION_ATTACHMENT = 'attachment'; const NEW_TAB_REL = [ 'noreferrer', 'noopener' ]; -const icon = ( - - - - - -); - const ImageURLInputUI = ( { linkDestination, onChangeUrl, @@ -45,6 +41,9 @@ const ImageURLInputUI = ( { linkTarget, linkClass, rel, + showLightboxSetting, + lightboxEnabled, + onSetLightbox, } ) => { const [ isOpen, setIsOpen ] = useState( false ); // Use internal state instead of a ref to make sure that the component @@ -138,6 +137,7 @@ const ImageURLInputUI = ( { onChangeUrl( { href: urlInput, linkDestination: selectedDestination, + lightbox: { enabled: false }, } ); } stopEditLink(); @@ -157,22 +157,17 @@ const ImageURLInputUI = ( { const linkDestinations = [ { linkDestination: LINK_DESTINATION_MEDIA, - title: __( 'Media File' ), + title: __( 'Link to image file' ), url: mediaType === 'image' ? mediaUrl : undefined, - icon, + icon: image, }, ]; if ( mediaType === 'image' && mediaLink ) { linkDestinations.push( { linkDestination: LINK_DESTINATION_ATTACHMENT, - title: __( 'Attachment Page' ), + title: __( 'Link to attachment page' ), url: mediaType === 'image' ? mediaLink : undefined, - icon: ( - - - - - ), + icon: page, } ); } return linkDestinations; @@ -225,7 +220,7 @@ const ImageURLInputUI = ( { /> @@ -233,6 +228,7 @@ const ImageURLInputUI = ( { ); const linkEditorValue = urlInput !== null ? urlInput : url; + const showLinkEditor = ( ! linkEditorValue && ! lightboxEnabled ) === true; const urlLabel = ( getLinkDestinations().find( @@ -245,7 +241,7 @@ const ImageURLInputUI = ( { advancedOptions } + renderSettings={ + ! lightboxEnabled ? () => advancedOptions : null + } additionalControls={ - ! linkEditorValue && ( - + showLinkEditor && ( + { getLinkDestinations().map( ( link ) => ( { setUrlInput( null ); onSetHref( link.url ); @@ -273,20 +272,45 @@ const ImageURLInputUI = ( { { link.title } ) ) } - + { showLightboxSetting && ( + { + setUrlInput( null ); + onChangeUrl( { + linkDestination: + LINK_DESTINATION_NONE, + href: '', + } ); + onSetLightbox( true ); + stopEditLink(); + } } + > + { __( 'Expand on click' ) } + + ) } + ) } > - { ( ! url || isEditingLink ) && ( - + { ( ! url || isEditingLink ) && ! lightboxEnabled && ( + <> + + ) } - { url && ! isEditingLink && ( + { url && ! isEditingLink && ! lightboxEnabled && ( <>