From 113eb475e25cb9ef2d303c12c1b8623950ab0786 Mon Sep 17 00:00:00 2001 From: Grant Kinney Date: Fri, 26 Jan 2024 12:41:04 -0600 Subject: [PATCH 001/209] Updates Font Families and Font Faces endpoints context param (#58287) --- .../class-wp-rest-font-faces-controller.php | 14 +++++----- ...class-wp-rest-font-families-controller.php | 27 ++++--------------- .../wpRestFontFacesController.php | 8 +++--- .../wpRestFontFamiliesController.php | 8 +++--- 4 files changed, 19 insertions(+), 38 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 fac32362325f4..68173dd43f47d 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 @@ -79,7 +79,7 @@ public function register_routes() { 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ), 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'edit' ) ), + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ), ), array( @@ -427,7 +427,7 @@ public function prepare_item_for_response( $item, $request ) { $data['font_face_settings'] = $this->get_settings_from_post( $item ); } - $context = ! empty( $request['context'] ) ? $request['context'] : 'edit'; + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); @@ -471,7 +471,7 @@ public function get_item_schema() { 'id' => array( 'description' => __( 'Unique identifier for the post.', 'default' ), 'type' => 'integer', - 'context' => array( 'edit', 'embed' ), + 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'theme_json_version' => array( @@ -480,19 +480,19 @@ public function get_item_schema() { 'default' => 2, 'minimum' => 2, 'maximum' => 2, - 'context' => array( 'edit', 'embed' ), + 'context' => array( 'view', 'edit', 'embed' ), ), 'parent' => array( 'description' => __( 'The ID for the parent font family of the font face.', 'gutenberg' ), 'type' => 'integer', - 'context' => array( 'edit', 'embed' ), + 'context' => array( 'view', 'edit', 'embed' ), ), // Font face settings come directly from theme.json schema // See https://schemas.wp.org/trunk/theme.json 'font_face_settings' => array( 'description' => __( 'font-face declaration in theme.json format.', 'gutenberg' ), 'type' => 'object', - 'context' => array( 'edit', 'embed' ), + 'context' => array( 'view', 'edit', 'embed' ), 'properties' => array( 'fontFamily' => array( 'description' => __( 'CSS font-family value.', 'gutenberg' ), @@ -601,8 +601,6 @@ public function get_item_schema() { public function get_collection_params() { $query_params = parent::get_collection_params(); - $query_params['context']['default'] = 'edit'; - // Remove unneeded params. unset( $query_params['after'] ); unset( $query_params['modified_after'] ); 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 887a8a5250cc3..4d5a89f6de1a0 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 @@ -230,7 +230,7 @@ public function prepare_item_for_response( $item, $request ) { $data['font_family_settings'] = $this->get_settings_from_post( $item ); } - $context = ! empty( $request['context'] ) ? $request['context'] : 'edit'; + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); @@ -274,7 +274,7 @@ public function get_item_schema() { 'id' => array( 'description' => __( 'Unique identifier for the post.', 'default' ), 'type' => 'integer', - 'context' => array( 'edit' ), + 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'theme_json_version' => array( @@ -283,12 +283,12 @@ public function get_item_schema() { 'default' => 2, 'minimum' => 2, 'maximum' => 2, - 'context' => array( 'edit' ), + 'context' => array( 'view', 'edit', 'embed' ), ), 'font_faces' => array( 'description' => __( 'The IDs of the child font faces in the font family.', 'gutenberg' ), 'type' => 'array', - 'context' => array( 'edit' ), + 'context' => array( 'view', 'edit', 'embed' ), 'items' => array( 'type' => 'integer', ), @@ -298,7 +298,7 @@ public function get_item_schema() { 'font_family_settings' => array( 'description' => __( 'font-face declaration in theme.json format.', 'gutenberg' ), 'type' => 'object', - 'context' => array( 'edit' ), + 'context' => array( 'view', 'edit', 'embed' ), 'properties' => array( 'name' => array( 'description' => 'Name of the font family preset, translatable.', @@ -338,8 +338,6 @@ public function get_item_schema() { public function get_collection_params() { $query_params = parent::get_collection_params(); - $query_params['context']['default'] = 'edit'; - // Remove unneeded params. unset( $query_params['after'] ); unset( $query_params['modified_after'] ); @@ -362,21 +360,6 @@ public function get_collection_params() { return apply_filters( 'rest_wp_font_family_collection_params', $query_params ); } - /** - * Retrieves the query params for the font family collection, defaulting to the 'edit' context. - * - * @since 6.5.0 - * - * @param array $args Optional. Additional arguments for context parameter. Default empty array. - * @return array Context parameter details. - */ - public function get_context_param( $args = array() ) { - if ( isset( $args['default'] ) ) { - $args['default'] = 'edit'; - } - return parent::get_context_param( $args ); - } - /** * Get the arguments used when creating or updating a font family. * diff --git a/phpunit/tests/fonts/font-library/wpRestFontFacesController.php b/phpunit/tests/fonts/font-library/wpRestFontFacesController.php index 1904a17228bdc..6091a3c91dc9e 100644 --- a/phpunit/tests/fonts/font-library/wpRestFontFacesController.php +++ b/phpunit/tests/fonts/font-library/wpRestFontFacesController.php @@ -136,16 +136,16 @@ public function test_context_param() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] ); - $this->assertSame( 'edit', $data['endpoints'][0]['args']['context']['default'] ); - $this->assertSame( array( 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); // Single. $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families/' . self::$font_family_id . '/font-faces/' . self::$font_face_id1 ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] ); - $this->assertSame( 'edit', $data['endpoints'][0]['args']['context']['default'] ); - $this->assertSame( array( 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); } /** diff --git a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php index 6e6a822a9881f..601ff32e2b6c9 100644 --- a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php +++ b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController.php @@ -151,16 +151,16 @@ public function test_context_param() { $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] ); - $this->assertSame( 'edit', $data['endpoints'][0]['args']['context']['default'] ); - $this->assertSame( array( 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); // Single. $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-families/' . self::$font_family_id1 ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); $this->assertArrayNotHasKey( 'allow_batch', $data['endpoints'][0] ); - $this->assertSame( 'edit', $data['endpoints'][0]['args']['context']['default'] ); - $this->assertSame( array( 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); + $this->assertSame( 'view', $data['endpoints'][0]['args']['context']['default'] ); + $this->assertSame( array( 'view', 'embed', 'edit' ), $data['endpoints'][0]['args']['context']['enum'] ); } From 970488826c004014862b3f92458e102db2201d88 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 26 Jan 2024 20:12:19 +0100 Subject: [PATCH 002/209] [RNMobile] Heading block: Add integration test to cover changing heading level (#58328) * Remove unneeded `await` statements when getting a block * Use `screen` from test helpers * Add test case to cover changing heading level * Formats integration test file of Heading block Co-authored-by: Siobhan Bamber --------- Co-authored-by: Siobhan Bamber --- .../test/__snapshots__/index.native.js.snap | 6 ++++ .../src/heading/test/index.native.js | 33 ++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/block-library/src/heading/test/__snapshots__/index.native.js.snap b/packages/block-library/src/heading/test/__snapshots__/index.native.js.snap index c0397e823d451..220061bf9a86b 100644 --- a/packages/block-library/src/heading/test/__snapshots__/index.native.js.snap +++ b/packages/block-library/src/heading/test/__snapshots__/index.native.js.snap @@ -1,5 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Heading block changes heading level 1`] = ` +" +
+" +`; + exports[`Heading block inserts block 1`] = ` "

diff --git a/packages/block-library/src/heading/test/index.native.js b/packages/block-library/src/heading/test/index.native.js index 1582e96aae0f4..498ae6b3ea1d7 100644 --- a/packages/block-library/src/heading/test/index.native.js +++ b/packages/block-library/src/heading/test/index.native.js @@ -2,11 +2,12 @@ * External dependencies */ import { + addBlock, fireEvent, + getBlock, getEditorHtml, initializeEditor, - addBlock, - getBlock, + screen, typeInRichText, waitFor, within, @@ -33,13 +34,13 @@ afterAll( () => { describe( 'Heading block', () => { it( 'inserts block', async () => { - const screen = await initializeEditor(); + await initializeEditor(); // Add block await addBlock( screen, 'Heading' ); // Get block - const headingBlock = await getBlock( screen, 'Heading' ); + const headingBlock = getBlock( screen, 'Heading' ); fireEvent.press( headingBlock ); expect( headingBlock ).toBeVisible(); @@ -48,7 +49,7 @@ describe( 'Heading block', () => { it( 'should set a text color', async () => { // Arrange - const screen = await initializeEditor(); + await initializeEditor(); await addBlock( screen, 'Heading' ); // Act @@ -86,7 +87,7 @@ describe( 'Heading block', () => { it( 'should set a background color', async () => { // Arrange - const screen = await initializeEditor(); + await initializeEditor(); await addBlock( screen, 'Heading' ); // Act @@ -120,9 +121,9 @@ describe( 'Heading block', () => { it( 'change level dropdown displays active selection', async () => { // Arrange - const screen = await initializeEditor(); + await initializeEditor(); await addBlock( screen, 'Heading' ); - const headingBlock = await getBlock( screen, 'Heading' ); + const headingBlock = getBlock( screen, 'Heading' ); // Act fireEvent.press( headingBlock ); @@ -136,9 +137,23 @@ describe( 'Heading block', () => { ).toBeVisible(); } ); + it( 'changes heading level', async () => { + // Arrange + await initializeEditor(); + await addBlock( screen, 'Heading' ); + + // Act + fireEvent.press( getBlock( screen, 'Heading' ) ); + fireEvent.press( screen.getByLabelText( 'Change level' ) ); + fireEvent.press( screen.getByLabelText( 'Heading 6' ) ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + it( 'should merge with an empty Paragraph block and keep being the Heading block', async () => { // Arrange - const screen = await initializeEditor(); + await initializeEditor(); await addBlock( screen, 'Paragraph' ); // Act From 208f44d11f19b2957ea742d76b2de2b2b1ed9b96 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Fri, 26 Jan 2024 19:45:53 -0300 Subject: [PATCH 003/209] Font Library: Improve font collection rest controller (#58222) * make WP_Font_Collection object properties public * remove class method no longer used * simplify the acces to the font collection properties * add schema, args, fields and pagination to font collections rest controller * add one test case for params * request a subset of font collection fields * lint * remove not needed default change * update version since * use placeholder for translation * update comment Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> * update translation domain Co-authored-by: Grant Kinney * update wording * set context to edit only * use 'view', 'edit' and 'embed' on the context of all the schema properties * prepare_item_for_response * default context param * args for item endpoint * format php --------- Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> Co-authored-by: Grant Kinney --- .../font-library/class-wp-font-collection.php | 44 ++-- .../font-library/class-wp-font-library.php | 4 +- ...ss-wp-rest-font-collections-controller.php | 234 +++++++++++++++++- .../font-library-modal/resolvers.js | 2 +- phpunit-watcher.yml.dist | 3 + .../wpFontCollection/getConfig.php | 76 ------ .../wpRestFontCollectionsController.php | 16 +- 7 files changed, 255 insertions(+), 124 deletions(-) delete mode 100644 phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php diff --git a/lib/experimental/fonts/font-library/class-wp-font-collection.php b/lib/experimental/fonts/font-library/class-wp-font-collection.php index 1ff96b1343b45..fedcfc2d02e6c 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-collection.php +++ b/lib/experimental/fonts/font-library/class-wp-font-collection.php @@ -27,7 +27,7 @@ class WP_Font_Collection { * * @var string */ - private $slug; + public $slug; /** * The name of the font collection. @@ -36,7 +36,7 @@ class WP_Font_Collection { * * @var string */ - private $name; + public $name; /** * Description of the font collection. @@ -45,7 +45,7 @@ class WP_Font_Collection { * * @var string */ - private $description; + public $description; /** * Source of the font collection. @@ -54,7 +54,7 @@ class WP_Font_Collection { * * @var string */ - private $src; + public $src; /** * Array of font families in the collection. @@ -63,7 +63,7 @@ class WP_Font_Collection { * * @var array */ - private $font_families; + public $font_families; /** * Categories associated with the font collection. @@ -72,7 +72,7 @@ class WP_Font_Collection { * * @var array */ - private $categories; + public $categories; /** @@ -138,34 +138,22 @@ public static function is_config_valid( $config ) { ( empty( $config['src'] ) && empty( $config['font_families'] ) ) || ( ! empty( $config['src'] ) && ! empty( $config['font_families'] ) ) ) { - _doing_it_wrong( __METHOD__, __( 'Font Collection config "src" option OR "font_families" option are required.', 'gutenberg' ), '6.5.0' ); + _doing_it_wrong( + __METHOD__, + sprintf( + /* translators: %1$s: src, %2$s: font_families */ + __( 'Font Collection config "%1$s" option OR "%2$s" option is required.', 'gutenberg' ), + 'src', + 'font_families' + ), + '6.5.0' + ); return false; } return true; } - /** - * Gets the font collection config. - * - * @since 6.5.0 - * - * @return array { - * An array of font collection config. - * - * @type string $slug The font collection's unique slug. - * @type string $name The font collection's name. - * @type string $description The font collection's description. - * } - */ - public function get_config() { - return array( - 'slug' => $this->slug, - 'name' => $this->name, - 'description' => $this->description, - ); - } - /** * Gets the font collection content. * diff --git a/lib/experimental/fonts/font-library/class-wp-font-library.php b/lib/experimental/fonts/font-library/class-wp-font-library.php index 51a84b957ea11..b826ffa62aad2 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-library.php +++ b/lib/experimental/fonts/font-library/class-wp-font-library.php @@ -69,7 +69,7 @@ public static function register_font_collection( $config ) { $new_collection = new WP_Font_Collection( $config ); - if ( self::is_collection_registered( $new_collection->get_config()['slug'] ) ) { + if ( self::is_collection_registered( $new_collection->slug ) ) { $error_message = sprintf( /* translators: %s: Font collection slug. */ __( 'Font collection with slug: "%s" is already registered.', 'gutenberg' ), @@ -82,7 +82,7 @@ public static function register_font_collection( $config ) { ); return new WP_Error( 'font_collection_registration_error', $error_message ); } - self::$collections[ $new_collection->get_config()['slug'] ] = $new_collection; + self::$collections[ $new_collection->slug ] = $new_collection; return $new_collection; } diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php index 51fd14fffaa95..dd8301c71371e 100644 --- a/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php +++ b/lib/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php @@ -44,7 +44,10 @@ public function register_routes() { 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), ) ); @@ -56,7 +59,11 @@ public function register_routes() { 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + ), ), + 'schema' => array( $this, 'get_public_item_schema' ), ) ); } @@ -68,13 +75,61 @@ public function register_routes() { * * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ - public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - $collections = array(); - foreach ( WP_Font_Library::get_font_collections() as $collection ) { - $collections[] = $collection->get_config(); + public function get_items( $request ) { + $collections_all = WP_Font_Library::get_font_collections(); + + $page = $request['page']; + $per_page = $request['per_page']; + $total_items = count( $collections_all ); + $max_pages = ceil( $total_items / $per_page ); + + if ( $page > $max_pages && $total_items > 0 ) { + return new WP_Error( + 'rest_post_invalid_page_number', + __( 'The page number requested is larger than the number of pages available.', 'default' ), + array( 'status' => 400 ) + ); + } + + $collections_page = array_slice( $collections_all, ( $page - 1 ) * $per_page, $per_page ); + + $items = array(); + foreach ( $collections_page as $collection ) { + $item = $this->prepare_item_for_response( $collection, $request ); + if ( is_wp_error( $item ) ) { + return $item; + } + $item = $this->prepare_response_for_collection( $item ); + $items[] = $item; + } + + $response = rest_ensure_response( $items ); + + $response->header( 'X-WP-Total', (int) $total_items ); + $response->header( 'X-WP-TotalPages', (int) $max_pages ); + + $request_params = $request->get_query_params(); + $collection_url = rest_url( $this->namespace . '/' . $this->rest_base ); + $base = add_query_arg( urlencode_deep( $request_params ), $collection_url ); + + if ( $page > 1 ) { + $prev_page = $page - 1; + + if ( $prev_page > $max_pages ) { + $prev_page = $max_pages; + } + + $prev_link = add_query_arg( 'page', $prev_page, $base ); + $response->link_header( 'prev', $prev_link ); + } + if ( $max_pages > $page ) { + $next_page = $page + 1; + $next_link = add_query_arg( 'page', $next_page, $base ); + + $response->link_header( 'next', $next_link ); } - return rest_ensure_response( $collections, 200 ); + return $response; } /** @@ -95,17 +150,170 @@ public function get_item( $request ) { return $collection; } - $config = $collection->get_config(); - $contents = $collection->get_content(); + $item = $this->prepare_item_for_response( $collection, $request ); - // If there was an error getting the collection data, return the error. - if ( is_wp_error( $contents ) ) { - $contents->add_data( array( 'status' => 500 ) ); - return $contents; + if ( is_wp_error( $item ) ) { + return $item; } - $collection_data = array_merge( $config, $contents ); - return rest_ensure_response( $collection_data ); + return $item; + } + + /* + * Prepare a single collection output for response. + * + * @since 6.5.0 + * + * @param WP_Font_Collection $collection Collection object. + * @param WP_REST_Request $request Request object. + * @return array|WP_Error + */ + public function prepare_item_for_response( $collection, $request ) { + $fields = $this->get_fields_for_response( $request ); + $item = array(); + + $config_fields = array( 'slug', 'name', 'description' ); + foreach ( $config_fields as $field ) { + if ( in_array( $field, $fields, true ) ) { + $item[ $field ] = $collection->$field; + } + } + + $data_fields = array( 'font_families', 'categories' ); + if ( in_array( 'font_families', $fields, true ) || in_array( 'categories', $fields, true ) ) { + $content = $collection->get_content(); + + // If there was an error getting the collection data, return the error. + if ( is_wp_error( $content ) ) { + $content->add_data( array( 'status' => 500 ) ); + return $content; + } + + foreach ( $data_fields as $field ) { + if ( in_array( $field, $fields, true ) ) { + $item[ $field ] = $content[ $field ]; + } + } + } + + $response = rest_ensure_response( $item ); + + if ( rest_is_field_included( '_links', $fields ) ) { + $links = $this->prepare_links( $collection ); + $response->add_links( $links ); + } + + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; + $response->data = $this->add_additional_fields_to_object( $response->data, $request ); + $response->data = $this->filter_response_by_context( $response->data, $context ); + + /** + * Filters a font collection returned from the REST API. + * + * Allows modification of the font collection right before it is returned. + * + * @since 6.5.0 + * + * @param WP_REST_Response $response The response object. + * @param WP_Font_Collection $collection The Font Collection object. + * @param WP_REST_Request $request Request used to generate the response. + */ + return apply_filters( 'rest_prepare_font_collection', $response, $collection, $request ); + } + + /** + * Retrieves the font collection's schema, conforming to JSON Schema. + * + * @since 6.5.0 + * + * @return array Item schema data. + */ + public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'font-collection', + 'type' => 'object', + 'properties' => array( + 'slug' => array( + 'description' => __( 'Unique identifier for the font collection.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, + ), + 'name' => array( + 'description' => __( 'The name for the font collection.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + ), + 'description' => array( + 'description' => __( 'The description for the font collection.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'view', 'edit', 'embed' ), + ), + 'font_families' => array( + 'description' => __( 'The font families for the font collection.', 'gutenberg' ), + 'type' => 'array', + 'context' => array( 'view', 'edit', 'embed' ), + ), + 'categories' => array( + 'description' => __( 'The categories for the font collection.', 'gutenberg' ), + 'type' => 'array', + 'context' => array( 'view', 'edit', 'embed' ), + ), + ), + ); + + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); + } + + /** + * Prepares links for the request. + * + * @since 6.5.0 + * + * @param WP_Font_Collection $collection Font collection data + * @return array Links for the given font collection. + */ + protected function prepare_links( $collection ) { + $links = array( + 'self' => array( + 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $collection->slug ) ), + ), + 'collection' => array( + 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), + ), + ); + return $links; + } + + /** + * Retrieves the search params for the font collections. + * + * @since 6.5.0 + * + * @return array Collection parameters. + */ + public function get_collection_params() { + $query_params = parent::get_collection_params(); + + $query_params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); + + unset( $query_params['search'] ); + + /** + * Filters REST API collection parameters for the font collections controller. + * + * @since 6.5.0 + * + * @param array $query_params JSON Schema-formatted collection parameters. + */ + return apply_filters( 'rest_font_collections_collection_params', $query_params ); } /** diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js b/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js index a75fc6cbe78ff..a114630cfc08d 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js @@ -63,7 +63,7 @@ export async function fetchUninstallFontFamily( fontFamilyId ) { export async function fetchFontCollections() { const config = { - path: FONT_COLLECTIONS_URL, + path: `${ FONT_COLLECTIONS_URL }?_fields=slug,name,description`, method: 'GET', }; return await apiFetch( config ); diff --git a/phpunit-watcher.yml.dist b/phpunit-watcher.yml.dist index f2f9da5fbcdbf..165a4d8e66e0b 100644 --- a/phpunit-watcher.yml.dist +++ b/phpunit-watcher.yml.dist @@ -6,3 +6,6 @@ watch: notifications: passingTests: false failingTests: false + +phpunit: + arguments: '--filter Tests_Fonts_WpFontCollection_GetContent' diff --git a/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php b/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php deleted file mode 100644 index 393de7d22614d..0000000000000 --- a/phpunit/tests/fonts/font-library/wpFontCollection/getConfig.php +++ /dev/null @@ -1,76 +0,0 @@ -assertSame( $expected_data, $collection->get_config() ); - } - - /** - * Data provider. - * - * @return array[] - */ - public function data_should_get_config() { - $mock_file = wp_tempnam( 'my-collection-data-' ); - file_put_contents( $mock_file, '{"this is mock data":true}' ); - - return array( - 'with a file' => array( - 'config' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'src' => $mock_file, - ), - 'expected_data' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - ), - ), - 'with a url' => array( - 'config' => array( - 'slug' => 'my-collection-with-url', - 'name' => 'My Collection with URL', - 'description' => 'My collection description', - 'src' => 'https://localhost/fonts/mock-font-collection.json', - ), - 'expected_data' => array( - 'slug' => 'my-collection-with-url', - 'name' => 'My Collection with URL', - 'description' => 'My collection description', - ), - ), - 'with font_families' => array( - 'config' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - 'font_families' => array( array() ), - ), - 'expected_data' => array( - 'slug' => 'my-collection', - 'name' => 'My Collection', - 'description' => 'My collection description', - ), - ), - ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php index 164f88f3f7b4b..f2140267672d2 100644 --- a/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php +++ b/phpunit/tests/fonts/font-library/wpRestFontCollectionsController.php @@ -154,10 +154,18 @@ public function test_prepare_item() { // Controller does not use test_prepare_item(). } - /** - * @doesNotPerformAssertions - */ public function test_get_item_schema() { - // Controller does not use test_get_item_schema(). + $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/font-collections' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + + $this->assertSame( 200, $response->get_status() ); + $properties = $data['schema']['properties']; + $this->assertCount( 5, $properties ); + $this->assertArrayHasKey( 'slug', $properties ); + $this->assertArrayHasKey( 'name', $properties ); + $this->assertArrayHasKey( 'description', $properties ); + $this->assertArrayHasKey( 'font_families', $properties ); + $this->assertArrayHasKey( 'categories', $properties ); } } From 9578f0da58240b6131316dec92b4eca22b31f760 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Fri, 26 Jan 2024 19:46:27 -0300 Subject: [PATCH 004/209] Rename the slug of the google fonts collection from 'default-font-collection' to 'google-fonts'. (#58331) No behaviour change is added. Only the slug name is updated. --- lib/experimental/fonts/font-library/font-library.php | 6 +++--- .../global-styles/font-library-modal/font-collection.js | 4 ++-- .../font-library-modal/google-fonts-confirm-dialog.js | 2 +- .../components/global-styles/font-library-modal/index.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/experimental/fonts/font-library/font-library.php b/lib/experimental/fonts/font-library/font-library.php index e9744da5958f4..0bf9374f9f945 100644 --- a/lib/experimental/fonts/font-library/font-library.php +++ b/lib/experimental/fonts/font-library/font-library.php @@ -132,14 +132,14 @@ function wp_unregister_font_collection( $collection_id ) { } -$default_font_collection = array( - 'slug' => 'default-font-collection', +$google_fonts = array( + 'slug' => 'google-fonts', 'name' => 'Google Fonts', 'description' => __( 'Add from Google Fonts. Fonts are copied to and served from your site.', 'gutenberg' ), 'src' => 'https://s.w.org/images/fonts/17.6/collections/google-fonts-with-preview.json', ); -wp_register_font_collection( $default_font_collection ); +wp_register_font_collection( $google_fonts ); // @core-merge: This code should probably go into Core's src/wp-includes/functions.php. if ( ! function_exists( 'wp_get_font_dir' ) ) { diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js b/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js index 2ee59b4ad5fd0..2ff6df3047363 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js @@ -36,12 +36,12 @@ const DEFAULT_CATEGORY = { name: __( 'All' ), }; function FontCollection( { slug } ) { - const requiresPermission = slug === 'default-font-collection'; + const requiresPermission = slug === 'google-fonts'; const getGoogleFontsPermissionFromStorage = () => { return ( window.localStorage.getItem( - 'wp-font-library-default-font-collection-permission' + 'wp-font-library-google-fonts-permission' ) === 'true' ); }; diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js b/packages/edit-site/src/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js index cb10ec89c4fc7..8d7954c54dbcc 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/google-fonts-confirm-dialog.js @@ -14,7 +14,7 @@ function GoogleFontsConfirmDialog() { const handleConfirm = () => { // eslint-disable-next-line no-undef window.localStorage.setItem( - 'wp-font-library-default-font-collection-permission', + 'wp-font-library-google-fonts-permission', 'true' ); window.dispatchEvent( new Event( 'storage' ) ); diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/index.js b/packages/edit-site/src/components/global-styles/font-library-modal/index.js index c2c2d05e0c15b..71966449eb616 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/index.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/index.js @@ -34,7 +34,7 @@ const tabsFromCollections = ( collections ) => collections.map( ( { slug, name } ) => ( { id: slug, title: - collections.length === 1 && slug === 'default-font-collection' + collections.length === 1 && slug === 'google-fonts' ? __( 'Install Fonts' ) : name, } ) ); From dfa9efd87ccbae98778afb295edf944565b9f57a Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Sat, 27 Jan 2024 00:30:25 +0100 Subject: [PATCH 005/209] Mobile Release v1.111.2 (#58274) * Release script: Update react-native-editor version to 1.111.0 * Release script: Update CHANGELOG for version 1.111.0 * Release script: Update podfile * [RNMobile] Video block: Fix logic for displaying empty state based on source presence (#58015) * Avoid displaying Video block empty state when a source is present * Add tests to cover empty state visibility logic * Update `react-native-editor` changelog * Remove unneeded `await` statements from Video block unit tests * Update `react-native-editor` changelog * Release script: Update react-native-editor version to 1.111.1 * Release script: Update CHANGELOG for version 1.111.1 * Release script: Update podfile * RNMobile: Avoid crashes by ensuring RichText value exists prior to `toString` calls (#58088) * [RNMobile] Remove `mediaFilesCollectionBlock` initial prop (#58140) * Remove `mediaFilesCollectionBlock` initial prop * Remove `enableMediaFilesCollectionBlocks` from Gutenberg demo app * Release script: Update react-native-editor version to 1.111.2 * Release script: Update CHANGELOG for version 1.111.2 * Release script: Update podfile --------- Co-authored-by: Siobhan --- package-lock.json | 6 +++--- packages/react-native-aztec/package.json | 2 +- packages/react-native-bridge/package.json | 2 +- packages/react-native-editor/CHANGELOG.md | 3 +++ packages/react-native-editor/ios/Podfile.lock | 8 ++++---- packages/react-native-editor/package.json | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index bc87083df6028..8a24733408e4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -55749,7 +55749,7 @@ }, "packages/react-native-aztec": { "name": "@wordpress/react-native-aztec", - "version": "1.111.1", + "version": "1.111.2", "license": "GPL-2.0-or-later", "dependencies": { "@wordpress/element": "file:../element", @@ -55762,7 +55762,7 @@ }, "packages/react-native-bridge": { "name": "@wordpress/react-native-bridge", - "version": "1.111.1", + "version": "1.111.2", "license": "GPL-2.0-or-later", "dependencies": { "@wordpress/react-native-aztec": "file:../react-native-aztec" @@ -55773,7 +55773,7 @@ }, "packages/react-native-editor": { "name": "@wordpress/react-native-editor", - "version": "1.111.1", + "version": "1.111.2", "hasInstallScript": true, "license": "GPL-2.0-or-later", "dependencies": { diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index bcc9613fddfe7..6090112b6a498 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.111.1", + "version": "1.111.2", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index 5008c428ad2b2..1f080b3ad95eb 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.111.1", + "version": "1.111.2", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 88d7c1d089aff..0a4b9c42e6355 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,6 +11,9 @@ For each user feature we should also add a importance categorization label to i ## Unreleased +## 1.111.2 +- [*] [internal] Remove `mediaFilesCollectionBlock` initial prop [#58140] + ## 1.111.1 - [**] Video block: Fix logic for displaying empty state based on source presence [#58015] - [**] Fix crash when RichText values are not defined [#58088] diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index 3852f073303b3..7299900ae7419 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.71.11) - fmt (6.2.1) - glog (0.3.5) - - Gutenberg (1.111.1): + - Gutenberg (1.111.2): - React-Core (= 0.71.11) - React-CoreModules (= 0.71.11) - React-RCTImage (= 0.71.11) @@ -429,7 +429,7 @@ PODS: - React-RCTImage - RNSVG (13.9.0): - React-Core - - RNTAztecView (1.111.1): + - RNTAztecView (1.111.2): - React-Core - WordPress-Aztec-iOS (= 1.19.9) - SDWebImage (5.11.1): @@ -617,7 +617,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: f07662560742d82a5b73cee116c70b0b49bcc220 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b - Gutenberg: c251b1260cafc0efee13c29c7b4a22e399e17be2 + Gutenberg: 20abf644079466c54ebcb491b8117e35f286cfb6 hermes-engine: 34c863b446d0135b85a6536fa5fd89f48196f848 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c @@ -662,7 +662,7 @@ SPEC CHECKSUMS: RNReanimated: d4f363f4987ae0ade3e36ff81c94e68261bf4b8d RNScreens: 68fd1060f57dd1023880bf4c05d74784b5392789 RNSVG: 53c661b76829783cdaf9b7a57258f3d3b4c28315 - RNTAztecView: 7f2d1f97d07c00efd7f24a687fd2d0c5aec44669 + RNTAztecView: 129120dde150fce9554f28272321310aa18a0d82 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d WordPress-Aztec-iOS: fbebd569c61baa252b3f5058c0a2a9a6ada686bb diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index f2a57980c86ec..d8ca44059bd22 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-editor", - "version": "1.111.1", + "version": "1.111.2", "description": "Mobile WordPress gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 9c030aad2cafdea575897c39586528da28772a1b Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Sat, 27 Jan 2024 22:57:54 +0200 Subject: [PATCH 006/209] InnerBlocks: fix continuous re-rendering on inner blocks change (#58348) --- .../src/components/inner-blocks/index.js | 6 ------ .../src/components/inner-blocks/index.native.js | 12 +++--------- .../inner-blocks/use-inner-block-template-sync.js | 4 +--- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 6461d57a7e604..4bb555d27f626 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -71,7 +71,6 @@ function UncontrolledInnerBlocks( props ) { layout, name, blockType, - innerBlocks, parentLock, } = props; @@ -92,7 +91,6 @@ function UncontrolledInnerBlocks( props ) { useInnerBlockTemplateSync( clientId, - innerBlocks, template, templateLock, templateInsertUpdatesSelection @@ -193,7 +191,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { hasOverlay, name, blockType, - innerBlocks, parentLock, parentClientId, isDropZoneDisabled, @@ -208,7 +205,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { isBlockSelected, hasSelectedInnerBlock, __unstableGetEditorMode, - getBlocks, getTemplateLock, getBlockRootClientId, __unstableIsWithinBlockOverlay, @@ -234,7 +230,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { enableClickThrough, name: blockName, blockType: getBlockType( blockName ), - innerBlocks: getBlocks( clientId ), parentLock: getTemplateLock( _parentClientId ), parentClientId: _parentClientId, isDropZoneDisabled: @@ -263,7 +258,6 @@ export function useInnerBlocksProps( props = {}, options = {} ) { layout, name, blockType, - innerBlocks, parentLock, ...options, }; diff --git a/packages/block-editor/src/components/inner-blocks/index.native.js b/packages/block-editor/src/components/inner-blocks/index.native.js index 2f04854bce2fc..1398a5abd51e4 100644 --- a/packages/block-editor/src/components/inner-blocks/index.native.js +++ b/packages/block-editor/src/components/inner-blocks/index.native.js @@ -105,17 +105,12 @@ function UncontrolledInnerBlocks( props ) { const context = useBlockContext( clientId ); - const { nestingLevel, innerBlocks, parentLock } = useSelect( + const { nestingLevel, parentLock } = useSelect( ( select ) => { - const { - getBlockParents, - getBlocks, - getTemplateLock, - getBlockRootClientId, - } = select( blockEditorStore ); + const { getBlockParents, getTemplateLock, getBlockRootClientId } = + select( blockEditorStore ); return { nestingLevel: getBlockParents( clientId )?.length, - innerBlocks: getBlocks( clientId ), parentLock: getTemplateLock( getBlockRootClientId( clientId ) ), }; }, @@ -139,7 +134,6 @@ function UncontrolledInnerBlocks( props ) { useInnerBlockTemplateSync( clientId, - innerBlocks, template, templateLock, templateInsertUpdatesSelection diff --git a/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js b/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js index 614ddad921ca4..020b391039a01 100644 --- a/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js +++ b/packages/block-editor/src/components/inner-blocks/use-inner-block-template-sync.js @@ -23,7 +23,6 @@ import { store as blockEditorStore } from '../../store'; * then we replace the inner blocks with the correct value after synchronizing it with the template. * * @param {string} clientId The block client ID. - * @param {Array} innerBlocks * @param {Object} template The template to match. * @param {string} templateLock The template lock state for the inner blocks. For * example, if the template lock is set to "all", @@ -37,7 +36,6 @@ import { store as blockEditorStore } from '../../store'; */ export default function useInnerBlockTemplateSync( clientId, - innerBlocks, template, templateLock, templateInsertUpdatesSelection @@ -112,5 +110,5 @@ export default function useInnerBlockTemplateSync( return () => { isCancelled = true; }; - }, [ innerBlocks, template, templateLock, clientId ] ); + }, [ template, templateLock, clientId ] ); } From 2cb8526791da837d6e17fb2c93bf3d8ba7801e39 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:54:28 +0200 Subject: [PATCH 007/209] Image block: remove a block editor store sub (#57232) --- packages/block-library/src/image/edit.js | 27 ++--- packages/block-library/src/image/image.js | 138 +++++++++------------- 2 files changed, 68 insertions(+), 97 deletions(-) diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index 3fed0819251e6..75db08a9ed48f 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -94,7 +94,7 @@ function hasSize( image, size ) { export function ImageEdit( { attributes, setAttributes, - isSelected, + isSelected: isSingleSelected, className, insertBlocksAfter, onReplace, @@ -142,14 +142,7 @@ export function ImageEdit( { }, [ align ] ); const ref = useRef(); - const { imageDefaultSize, mediaUpload } = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - const settings = getSettings(); - return { - imageDefaultSize: settings.imageDefaultSize, - mediaUpload: settings.mediaUpload, - }; - }, [] ); + const { getSettings } = useSelect( blockEditorStore ); const blockEditingMode = useBlockEditingMode(); const { createErrorNotice } = useDispatch( noticesStore ); @@ -183,6 +176,8 @@ export function ImageEdit( { setTemporaryURL(); + const { imageDefaultSize } = getSettings(); + // Try to use the previous selected image size if its available // otherwise try the default image size or fallback to "full" let newSize = 'full'; @@ -265,7 +260,7 @@ export function ImageEdit( { setAttributes( { url: newURL, id: undefined, - sizeSlug: imageDefaultSize, + sizeSlug: getSettings().imageDefaultSize, } ); } } @@ -281,6 +276,10 @@ export function ImageEdit( { const file = getBlobByURL( url ); if ( file ) { + const { mediaUpload } = getSettings(); + if ( ! mediaUpload ) { + return; + } mediaUpload( { filesList: [ file ], onFileChange: ( [ img ] ) => { @@ -336,7 +335,7 @@ export function ImageEdit( { // Much of this description is duplicated from MediaPlaceholder. const { lockUrlControls = false } = useSelect( ( select ) => { - if ( ! isSelected ) { + if ( ! isSingleSelected ) { return {}; } @@ -352,14 +351,14 @@ export function ImageEdit( { )?.lockAttributesEditing === true, }; }, - [ isSelected ] + [ isSingleSelected ] ); const placeholder = ( content ) => { return ( { - const { getMedia } = select( coreStore ); - return { - image: - id && isSelected - ? getMedia( id, { context: 'view' } ) - : null, - }; - }, - [ id, isSelected ] + const { getBlock, getSettings } = useSelect( blockEditorStore ); + + const image = useSelect( + ( select ) => + id && isSingleSelected + ? select( coreStore ).getMedia( id, { context: 'view' } ) + : null, + [ id, isSingleSelected ] ); - const { - canInsertCover, - imageEditing, - imageSizes, - maxWidth, - mediaUpload, - multiImageSelection, - } = useSelect( + const { canInsertCover, imageEditing, imageSizes, maxWidth } = useSelect( ( select ) => { - const { - getBlockRootClientId, - getMultiSelectedBlockClientIds, - getBlockName, - getSettings, - canInsertBlockType, - } = select( blockEditorStore ); + const { getBlockRootClientId, canInsertBlockType } = + select( blockEditorStore ); const rootClientId = getBlockRootClientId( clientId ); const settings = getSettings(); - const multiSelectedClientIds = getMultiSelectedBlockClientIds(); return { imageEditing: settings.imageEditing, imageSizes: settings.imageSizes, maxWidth: settings.maxWidth, - mediaUpload: settings.mediaUpload, canInsertCover: canInsertBlockType( 'core/cover', rootClientId ), - multiImageSelection: - multiSelectedClientIds.length && - multiSelectedClientIds.every( - ( _clientId ) => - getBlockName( _clientId ) === 'core/image' - ), }; }, [ clientId ] @@ -212,7 +187,6 @@ export default function Image( { ( { slug } ) => image?.media_details?.sizes?.[ slug ]?.source_url ) .map( ( { name, slug } ) => ( { value: slug, label: name } ) ); - const canUploadMedia = !! mediaUpload; // If an image is externally hosted, try to fetch the image data. This may // fail if the image host doesn't allow CORS with the domain. If it works, @@ -220,8 +194,8 @@ export default function Image( { useEffect( () => { if ( ! isExternalImage( id, url ) || - ! isSelected || - ! canUploadMedia + ! isSingleSelected || + ! getSettings().mediaUpload ) { setExternalBlob(); return; @@ -236,7 +210,7 @@ export default function Image( { .then( ( blob ) => setExternalBlob( blob ) ) // Do nothing, cannot upload. .catch( () => {} ); - }, [ id, url, isSelected, externalBlob, canUploadMedia ] ); + }, [ id, url, isSingleSelected, externalBlob ] ); // Get naturalWidth and naturalHeight from image ref, and fall back to loaded natural // width and height. This resolves an issue in Safari where the loaded natural @@ -304,6 +278,10 @@ export default function Image( { } function uploadExternal() { + const { mediaUpload } = getSettings(); + if ( ! mediaUpload ) { + return; + } mediaUpload( { filesList: [ externalBlob ], onFileChange( [ img ] ) { @@ -326,13 +304,13 @@ export default function Image( { } useEffect( () => { - if ( ! isSelected ) { + if ( ! isSingleSelected ) { setIsEditingImage( false ); } - }, [ isSelected ] ); + }, [ isSingleSelected ] ); const canEditImage = id && naturalWidth && naturalHeight && imageEditing; - const allowCrop = ! multiImageSelection && canEditImage && ! isEditingImage; + const allowCrop = isSingleSelected && canEditImage && ! isEditingImage; function switchToCover() { replaceBlocks( @@ -418,7 +396,7 @@ export default function Image( { lockTitleControls = false, } = useSelect( ( select ) => { - if ( ! isSelected ) { + if ( ! isSingleSelected ) { return {}; } @@ -445,26 +423,24 @@ export default function Image( { ?.lockAttributesEditing === true, }; }, - [ isSelected ] + [ isSingleSelected ] ); const controls = ( <> - { ! multiImageSelection && - ! isEditingImage && - ! lockUrlControls && ( - - ) } + { isSingleSelected && ! isEditingImage && ! lockUrlControls && ( + + ) } { allowCrop && ( setIsEditingImage( true ) } @@ -472,7 +448,7 @@ export default function Image( { label={ __( 'Crop' ) } /> ) } - { ! multiImageSelection && canInsertCover && ( + { isSingleSelected && canInsertCover && ( ) } - { ! multiImageSelection && - ! isEditingImage && - ! lockUrlControls && ( - - - - ) } - { ! multiImageSelection && externalBlob && ( + { isSingleSelected && ! isEditingImage && ! lockUrlControls && ( + + + + ) } + { isSingleSelected && externalBlob && ( - { ! multiImageSelection && ( + { isSingleSelected && ( ); From 9ad73041b644076927e454b595ecd867953ea12d Mon Sep 17 00:00:00 2001 From: Nick Diego Date: Sun, 28 Jan 2024 15:15:28 -0600 Subject: [PATCH 008/209] Update link to the correct fundamentals doc. (#58352) --- docs/getting-started/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/tutorial.md b/docs/getting-started/tutorial.md index 11dde6506472f..323aa8fc3ac65 100644 --- a/docs/getting-started/tutorial.md +++ b/docs/getting-started/tutorial.md @@ -715,7 +715,7 @@ Compare this to a statically rendered block like the Paragraph block. The HTML of the paragraph is stored in post content and saved in the database. -You can learn more about dynamic and static rendering in the [Fundamentals documentation](https://developer.wordpress.org/block-editor/getting-started/fundamentals/). While most blocks are either dynamically or statically rendered, you can build a block that utilizes both methods. +You can learn more about dynamic and static rendering in the [Fundamentals documentation](https://developer.wordpress.org/block-editor/getting-started/fundamentals/static-dynamic-rendering/). While most blocks are either dynamically or statically rendered, you can build a block that utilizes both methods. ### Why add static rendering? From 5e2b0c374531c656d9782d0dc3602cea4d778947 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 29 Jan 2024 08:56:13 +0400 Subject: [PATCH 009/209] Block Editor: Hide the 'Content' panel for locked blocks when there's no content (#58259) --- .../block-editor/src/components/block-inspector/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index a52b8b9a01507..7b8e5296c4502 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -56,9 +56,11 @@ function BlockInspectorLockedBlocks( { topLevelLockedBlock } ) { /> - - - + { contentClientIds.length > 0 && ( + + + + ) } ); } From 0bb25edda5a0c7279457548a1402024b84229a82 Mon Sep 17 00:00:00 2001 From: Madhu Dollu Date: Mon, 29 Jan 2024 10:54:09 +0530 Subject: [PATCH 010/209] remove additional wrapper around getShadowClassesAndStyles (#58297) --- packages/block-editor/src/hooks/index.js | 2 +- packages/block-editor/src/hooks/index.native.js | 2 +- .../block-editor/src/hooks/use-shadow-props.js | 14 -------------- packages/block-editor/src/index.js | 1 - packages/block-library/src/button/edit.js | 2 +- 5 files changed, 3 insertions(+), 18 deletions(-) diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index e3c0a7580aab3..e6227ea2b03e2 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -71,7 +71,7 @@ createBlockSaveFilter( [ export { useCustomSides } from './dimensions'; export { useLayoutClasses, useLayoutStyles } from './layout'; export { getBorderClassesAndStyles, useBorderProps } from './use-border-props'; -export { getShadowClassesAndStyles, useShadowProps } from './use-shadow-props'; +export { getShadowClassesAndStyles } from './use-shadow-props'; export { getColorClassesAndStyles, useColorProps } from './use-color-props'; export { getSpacingClassesAndStyles } from './use-spacing-props'; export { getTypographyClassesAndStyles } from './use-typography-props'; diff --git a/packages/block-editor/src/hooks/index.native.js b/packages/block-editor/src/hooks/index.native.js index 6e4c1c6a17ba5..c7f9df868f2bd 100644 --- a/packages/block-editor/src/hooks/index.native.js +++ b/packages/block-editor/src/hooks/index.native.js @@ -28,7 +28,7 @@ createBlockSaveFilter( [ ] ); export { getBorderClassesAndStyles, useBorderProps } from './use-border-props'; -export { getShadowClassesAndStyles, useShadowProps } from './use-shadow-props'; +export { getShadowClassesAndStyles } from './use-shadow-props'; export { getColorClassesAndStyles, useColorProps } from './use-color-props'; export { getSpacingClassesAndStyles } from './use-spacing-props'; export { useCachedTruthy } from './use-cached-truthy'; diff --git a/packages/block-editor/src/hooks/use-shadow-props.js b/packages/block-editor/src/hooks/use-shadow-props.js index fdc601366245c..34fb8b4546752 100644 --- a/packages/block-editor/src/hooks/use-shadow-props.js +++ b/packages/block-editor/src/hooks/use-shadow-props.js @@ -18,20 +18,6 @@ export function getShadowClassesAndStyles( attributes ) { const shadow = attributes.style?.shadow || ''; return { - className: undefined, style: getInlineStyles( { shadow } ), }; } - -/** - * Derives the shadow related props for a block from its shadow block support - * attributes. - * - * @param {Object} attributes Block attributes. - * - * @return {Object} ClassName & style props from shadow block support. - */ -export function useShadowProps( attributes ) { - const shadowProps = getShadowClassesAndStyles( attributes ); - return shadowProps; -} diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js index 83475b9358723..10c1fb1077c56 100644 --- a/packages/block-editor/src/index.js +++ b/packages/block-editor/src/index.js @@ -12,7 +12,6 @@ export { getSpacingClassesAndStyles as __experimentalGetSpacingClassesAndStyles, getGapCSSValue as __experimentalGetGapCSSValue, getShadowClassesAndStyles as __experimentalGetShadowClassesAndStyles, - useShadowProps as __experimentalUseShadowProps, useCachedTruthy, } from './hooks'; export * from './components'; diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index 7dffdfb5c1b66..4b32ad0a0ed67 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -33,7 +33,7 @@ import { __experimentalUseBorderProps as useBorderProps, __experimentalUseColorProps as useColorProps, __experimentalGetSpacingClassesAndStyles as useSpacingProps, - __experimentalUseShadowProps as useShadowProps, + __experimentalGetShadowClassesAndStyles as useShadowProps, __experimentalLinkControl as LinkControl, __experimentalGetElementClassName, store as blockEditorStore, From fcf0b5d4cb15ec521c562fc367c3fa6c1220013c Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:07:03 +0200 Subject: [PATCH 011/209] Block editor: prevent isSubtreeDisabled call if not needed (#58349) --- .../block-editor/src/components/block-list/block.js | 11 ++++++----- packages/block-editor/src/store/private-selectors.js | 9 +++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js index 7690386764f6c..deef2d75386fd 100644 --- a/packages/block-editor/src/components/block-list/block.js +++ b/packages/block-editor/src/components/block-list/block.js @@ -555,7 +555,7 @@ function BlockListBlockProvider( props ) { const typing = isTyping(); const hasLightBlockWrapper = blockType?.apiVersion > 1; const movingClientId = hasBlockMovingClientId(); - + const blockEditingMode = getBlockEditingMode( clientId ); return { mode: getBlockMode( clientId ), isSelectionEnabled: isSelectionEnabled(), @@ -574,7 +574,7 @@ function BlockListBlockProvider( props ) { themeSupportsLayout: supportsLayout, isTemporarilyEditingAsBlocks: __unstableGetTemporarilyEditingAsBlocks() === clientId, - blockEditingMode: getBlockEditingMode( clientId ), + blockEditingMode, mayDisplayControls: _isSelected || ( isFirstMultiSelectedBlock( clientId ) && @@ -590,7 +590,9 @@ function BlockListBlockProvider( props ) { index: getBlockIndex( clientId ), blockApiVersion: blockType?.apiVersion || 1, blockTitle: match?.title || blockType?.title, - isSubtreeDisabled: isBlockSubtreeDisabled( clientId ), + isSubtreeDisabled: + blockEditingMode === 'disabled' && + isBlockSubtreeDisabled( clientId ), isOutlineEnabled: outlineMode, hasOverlay: __unstableHasActiveBlockOverlayActive( clientId ), initialPosition: @@ -614,8 +616,7 @@ function BlockListBlockProvider( props ) { getBlockName( movingClientId ), getBlockRootClientId( clientId ) ), - isEditingDisabled: - getBlockEditingMode( clientId ) === 'disabled', + isEditingDisabled: blockEditingMode === 'disabled', className: hasLightBlockWrapper ? attributes.className : undefined, diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js index caca9507f0770..da32272c8ff23 100644 --- a/packages/block-editor/src/store/private-selectors.js +++ b/packages/block-editor/src/store/private-selectors.js @@ -45,13 +45,13 @@ export function getLastInsertedBlocksClientIds( state ) { } /** - * Returns true if the block with the given client ID and all of its descendants + * Returns true if all of the descendants of a block with the given client ID * have an editing mode of 'disabled', or false otherwise. * * @param {Object} state Global application state. * @param {string} clientId The block client ID. * - * @return {boolean} Whether the block and its descendants are disabled. + * @return {boolean} Whether the block descendants are disabled. */ export const isBlockSubtreeDisabled = createSelector( ( state, clientId ) => { @@ -63,10 +63,7 @@ export const isBlockSubtreeDisabled = createSelector( ) ); }; - return ( - getBlockEditingMode( state, clientId ) === 'disabled' && - getBlockOrder( state, clientId ).every( isChildSubtreeDisabled ) - ); + return getBlockOrder( state, clientId ).every( isChildSubtreeDisabled ); }, ( state ) => [ state.blocks.parents, From 9363cf0f60b35b9fcbaa4bc1edd07260f15b5ffd Mon Sep 17 00:00:00 2001 From: Maggie Date: Mon, 29 Jan 2024 10:37:10 +0100 Subject: [PATCH 012/209] Fix Placeholder component padding when body text font size is changed (#58323) * added font size to component wrapper so it's used by the padding value * update changelog --- packages/components/CHANGELOG.md | 1 + packages/components/src/placeholder/style.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 6ce2f99fab99f..a7c0ea58712ad 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -4,6 +4,7 @@ ### Bug Fix +- `Placeholder`: Fix Placeholder component padding when body text font size is changed ([#58323](https://github.com/WordPress/gutenberg/pull/58323)). - `Placeholder`: Fix Global Styles typography settings bleeding into placeholder component ([#58303](https://github.com/WordPress/gutenberg/pull/58303)). - `PaletteEdit`: Fix palette item accessibility in details view ([#58214](https://github.com/WordPress/gutenberg/pull/58214)). diff --git a/packages/components/src/placeholder/style.scss b/packages/components/src/placeholder/style.scss index 5bb0e0bb2ef11..8c6ca72112300 100644 --- a/packages/components/src/placeholder/style.scss +++ b/packages/components/src/placeholder/style.scss @@ -1,5 +1,6 @@ // This needs specificity to override individual block styles. .components-placeholder.components-placeholder { + font-size: $default-font-size; box-sizing: border-box; position: relative; padding: 1em; From 252418b734f0204ede36aeafebb90247523bb071 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Mon, 29 Jan 2024 12:47:38 +0100 Subject: [PATCH 013/209] 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 18f8d279c9946..01f8a506addc7 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 49b6b28b91a96..666e358cde60d 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 250d3bde6084c..38849f53d7f95 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 c133eb9981880..383bcaa1a4ae8 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 5dd9192c85566..2aadcbf79a158 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 014/209] 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 099c6b30222ef..0f6f7c33a91fa 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 015/209] 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 65ddb7278e703..9540e329596c9 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 e52b4f289ccdd..6d8951b5660ab 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 e1b91364fe22e..7888973397f6c 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 69b04fe4c4904..8e7e91dc13988 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 94aac654097e5..c6c847cbddcd1 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 e29b90b5e3c5c..8ec7275c0e9f8 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 4b32ad0a0ed67..c3a2aff1bd0d9 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 75db08a9ed48f..bef3b1198a25f 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 ee38ec5cfe7fd..81193cf66e19d 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 17f5e1837e35e..091491b2ed00c 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 f5ac19bc05f3d..0f6cde5ce7d68 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 ee60091e057c0..541f2f1600ad2 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 016/209] 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 e0a3b9639cf38..2a4ac7d0f76ca 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 017/209] 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 a0672a0bed580..574d802b183ad 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 018/209] 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 603a7b30de2ed..8dad4bc05039e 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 a1b87d3b38d88..a6c2864ad98f3 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 019/209] 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 574d802b183ad..70cf02e714cf8 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 020/209] 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 69c9a3f39f8a7..5bbc87ef53771 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 021/209] 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 70c389e4ec8db..9a4d4c01b5bcd 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 022/209] 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 8dad4bc05039e..73dd87eeab5a5 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 1125bbb1919b5..442ea7c492889 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 d6e7ebe72df21..2ea83c2863a0f 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 023/209] 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 94d4ea67d8cf9..b90b466853079 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 f89257f52d044..6e786a2ff6322 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 bec9bbd871cba..7af276d242509 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 9cf6d2414926a..46115d5b6c629 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 0f6f7c33a91fa..7964f86a5e782 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 4dc420bd41885..fde85ae72a316 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 dce160dfb2fea..0b9d2fe148ddd 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 bb73d085a8f48..821d6d1932ecc 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 a11decd201e94..e4a1a3c3a15d9 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 9837dda14e681..aba2923306497 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 b3fbd0ccee69c..97ced49e70b22 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 4b22b26fd4755..e2bcbe067c70c 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 0c6451f4959a4..fa5212822cc71 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 7fded64837de9..bea513a3785dc 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 a5425c55381f9..bfeeb792a7fa0 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 4a646ce836223..e2a836ae35f6c 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 8979d814a8da0..49531325247b7 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 06997c2ac23f8..0857aaac45d9a 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 7733a76280752..46cbd3a94831d 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 cf2e77c08d2e8..854cc8ce15214 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 e2fb9e4c9e3b0..a7dcf36cf4ad7 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 e1d29d517a5ff..ad294de93e77b 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 d8f2fe31aef9d..94a11217d5139 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 f6936e8ecfe36..ecf170c3697f7 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 36817a5e1c35b..eef6af390de78 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 c712bc4000c36..ff13309d1e4e7 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 19258213f26e5..4bb5429cf8c04 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 e2ad08c2a99ab..61c5eb32aed16 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 7f4f2ce86c425..5035d5611e32e 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 8f21f781396c6..34b35d61a7fa6 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 85bdb7dd6cf32..558f0e3496eff 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 35c0eee93308f..54ad72be30d7c 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 e32a9ba9b495f..973e9486bce6a 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 7598eba5c1cac..e051c2e67e7e5 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 20206511a4c96..1aea3684d0c63 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 03bcd59573aec..38a68e7f2dab2 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 8236eef081477..f49a672a7de8f 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 5658361d7aab6..b75a7b295fe29 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 024/209] 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 e7965c8efe4da..84622211e5a20 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 025/209] 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 70cf02e714cf8..2c7a818cc4f4c 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 026/209] 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 94c1d119c354c..4fefed8e3497e 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 668e8b101be92..cc8b0589644bd 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 4bb555d27f626..442f2568ce73d 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 6f791a42bf1fe..b0410b404d5e9 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 c9174cf35df5f..0000000000000 --- 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 0000000000000..819ad51f4b0e9 --- /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 da32272c8ff23..77374abcb62e4 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 0000000000000..7154b3edc1d73 --- /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 027/209] 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 68173dd43f47d..f35204517ab87 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 4d5a89f6de1a0..d00f1bd9d24a9 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 0bf9374f9f945..59c2552e54c94 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 028/209] 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 b291a8f1ee348..b75f70bd80de8 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 f35204517ab87..c727e35419e93 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 d00f1bd9d24a9..90d0e075bdefb 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 59c2552e54c94..5af21f11ebbb2 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 4b2b4d5d8b0db..6ded190b04e3f 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 0bd5b47ea2378..0000000000000 --- 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 21517a0970f93..0000000000000 --- 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 485587060f16a..579baa2d248e0 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 1f87d0d2fd5a1..ded19749b9123 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 6091a3c91dc9e..d6a95814b205a 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 029/209] 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 a6c2864ad98f3..b95489b480f13 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 030/209] 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 8ec7275c0e9f8..b76b655ee0d94 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 031/209] 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 61df542cf5840..21fc489b8a2c6 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 7caa218658b24..f6946b2c8e65d 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 && ( <>