From 7c4fe12dbf83306121b37b7f3668caf79a7bb2d7 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Mon, 12 Dec 2022 10:42:03 +0100 Subject: [PATCH 01/73] Columns mobile edit: remove unused updateBlockSettings action bind (#46455) --- packages/block-library/src/columns/edit.native.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/block-library/src/columns/edit.native.js b/packages/block-library/src/columns/edit.native.js index 96855e3da17e46..98b04cdace4a69 100644 --- a/packages/block-library/src/columns/edit.native.js +++ b/packages/block-library/src/columns/edit.native.js @@ -332,11 +332,6 @@ const ColumnsEditContainerWrapper = withDispatch( width: value, } ); }, - updateBlockSettings( settings ) { - const { clientId } = ownProps; - const { updateBlockListSettings } = dispatch( blockEditorStore ); - updateBlockListSettings( clientId, settings ); - }, /** * Updates the column columnCount, including necessary revisions to child Column * blocks to grant required or redistribute available space. From 669049484acfb36e0acd84eab68357938d6da7bd Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Mon, 12 Dec 2022 11:03:11 +0100 Subject: [PATCH 02/73] Columns mobile block: avoid returning unstable innerWidths from useSelect (#46403) --- .../block-library/src/columns/edit.native.js | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/block-library/src/columns/edit.native.js b/packages/block-library/src/columns/edit.native.js index 98b04cdace4a69..6331097f96b991 100644 --- a/packages/block-library/src/columns/edit.native.js +++ b/packages/block-library/src/columns/edit.native.js @@ -2,7 +2,7 @@ * External dependencies */ import { View, Dimensions } from 'react-native'; -import { map } from 'lodash'; + /** * WordPress dependencies */ @@ -445,7 +445,7 @@ const ColumnsEdit = ( props ) => { const { columnCount, isDefaultColumns, - innerWidths = [], + innerBlocks, hasParents, parentBlockAlignment, editorSidebarOpened, @@ -458,24 +458,20 @@ const ColumnsEdit = ( props ) => { getBlockAttributes, } = select( blockEditorStore ); const { isEditorSidebarOpened } = select( 'core/edit-post' ); - const innerBlocks = getBlocks( clientId ); - const isContentEmpty = map( - innerBlocks, - ( innerBlock ) => innerBlock.innerBlocks.length + const innerBlocksList = getBlocks( clientId ); + + const isContentEmpty = innerBlocksList.every( + ( innerBlock ) => innerBlock.innerBlocks.length === 0 ); - const innerColumnsWidths = innerBlocks.map( ( inn ) => ( { - clientId: inn.clientId, - attributes: { width: inn.attributes.width }, - } ) ); const parents = getBlockParents( clientId, true ); return { columnCount: getBlockCount( clientId ), - isDefaultColumns: ! isContentEmpty.filter( Boolean ).length, - innerWidths: innerColumnsWidths, - hasParents: !! parents.length, + isDefaultColumns: isContentEmpty, + innerBlocks: innerBlocksList, + hasParents: parents.length > 0, parentBlockAlignment: getBlockAttributes( parents[ 0 ] )?.align, editorSidebarOpened: isSelected && isEditorSidebarOpened(), }; @@ -483,13 +479,14 @@ const ColumnsEdit = ( props ) => { [ clientId, isSelected ] ); - const memoizedInnerWidths = useMemo( () => { - return innerWidths; - }, [ - // The JSON.stringify is used because innerWidth is always a new reference. - // The innerBlocks is a new reference after each attribute change of any nested block. - JSON.stringify( innerWidths ), - ] ); + const innerWidths = useMemo( + () => + innerBlocks.map( ( inn ) => ( { + clientId: inn.clientId, + attributes: { width: inn.attributes.width }, + } ) ), + [ innerBlocks ] + ); const [ isVisible, setIsVisible ] = useState( false ); @@ -509,7 +506,7 @@ const ColumnsEdit = ( props ) => { Date: Mon, 12 Dec 2022 12:36:04 +0200 Subject: [PATCH 03/73] [Patterns]: Add new pattern categories (#46144) * [Patterns]: Add new pattern categories * remove some categories * update php code * update php * add php test and update/remove test that are in core * map more categories * migrate only `columns` to `text` and show `text` at the bottom of categories list * address feedback --- ...rg-rest-block-patterns-controller-6-1.php} | 99 +------------- lib/compat/wordpress-6.1/rest-api.php | 9 -- lib/compat/wordpress-6.2/block-patterns.php | 102 ++++++++++++--- ...erg-rest-block-patterns-controller-6-2.php | 107 +++++++++++++++ lib/compat/wordpress-6.2/rest-api.php | 9 ++ lib/load.php | 3 +- .../components/inserter/block-patterns-tab.js | 11 +- ...rg-rest-block-patterns-controller-test.php | 123 +++++++++++++----- 8 files changed, 305 insertions(+), 158 deletions(-) rename lib/compat/wordpress-6.1/{class-gutenberg-rest-block-patterns-controller.php => class-gutenberg-rest-block-patterns-controller-6-1.php} (60%) create mode 100644 lib/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php diff --git a/lib/compat/wordpress-6.1/class-gutenberg-rest-block-patterns-controller.php b/lib/compat/wordpress-6.1/class-gutenberg-rest-block-patterns-controller-6-1.php similarity index 60% rename from lib/compat/wordpress-6.1/class-gutenberg-rest-block-patterns-controller.php rename to lib/compat/wordpress-6.1/class-gutenberg-rest-block-patterns-controller-6-1.php index c053a678083da8..b40f3aa2497f16 100644 --- a/lib/compat/wordpress-6.1/class-gutenberg-rest-block-patterns-controller.php +++ b/lib/compat/wordpress-6.1/class-gutenberg-rest-block-patterns-controller-6-1.php @@ -1,6 +1,6 @@ namespace = 'wp/v2'; - $this->rest_base = 'block-patterns/patterns'; - } - - /** - * Registers the routes for the objects of the controller. - * - * @since 6.0.0 - */ - public function register_routes() { - register_rest_route( - $this->namespace, - '/' . $this->rest_base, - array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ), - true - ); - } - - /** - * Checks whether a given request has permission to read block patterns. - * - * @since 6.0.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return true|WP_Error True if the request has read access, WP_Error object otherwise. - */ - public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - if ( current_user_can( 'edit_posts' ) ) { - return true; - } - - foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) { - if ( current_user_can( $post_type->cap->edit_posts ) ) { - return true; - } - } - - return new WP_Error( - 'rest_cannot_view', - __( 'Sorry, you are not allowed to view the registered block patterns.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - /** - * Retrieves all block patterns. - * - * @since 6.0.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function get_items( $request ) { - if ( ! $this->remote_patterns_loaded ) { - // Load block patterns from w.org. - _load_remote_block_patterns(); // Patterns with the `core` keyword. - _load_remote_featured_patterns(); // Patterns in the `featured` category. - _register_remote_theme_patterns(); // Patterns requested by current theme. - - $this->remote_patterns_loaded = true; - } - - $response = array(); - $patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); - foreach ( $patterns as $pattern ) { - $prepared_pattern = $this->prepare_item_for_response( $pattern, $request ); - $response[] = $this->prepare_response_for_collection( $prepared_pattern ); - } - return rest_ensure_response( $response ); - } - +class Gutenberg_REST_Block_Patterns_Controller_6_1 extends WP_REST_Block_Patterns_Controller { /** * Prepare a raw block pattern before it gets output in a REST API response. * * @since 6.0.0 + * @since 6.1.0 Added `postTypes` property. * * @param array $item Raw pattern as registered, before any changes. * @param WP_REST_Request $request Request object. @@ -147,6 +55,7 @@ public function prepare_item_for_response( $item, $request ) { * Retrieves the block pattern schema, conforming to JSON Schema. * * @since 6.0.0 + * @since 6.1.0 Added `post_types` property. * * @return array Item schema data. */ diff --git a/lib/compat/wordpress-6.1/rest-api.php b/lib/compat/wordpress-6.1/rest-api.php index f3391389d9e38a..5dd06d3aad855b 100644 --- a/lib/compat/wordpress-6.1/rest-api.php +++ b/lib/compat/wordpress-6.1/rest-api.php @@ -35,15 +35,6 @@ function gutenberg_update_post_types_rest_response( $response, $post_type ) { } add_filter( 'rest_prepare_post_type', 'gutenberg_update_post_types_rest_response', 10, 2 ); -/** - * Registers the block patterns REST API routes. - */ -function gutenberg_register_gutenberg_rest_block_patterns() { - $block_patterns = new Gutenberg_REST_Block_Patterns_Controller(); - $block_patterns->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_gutenberg_rest_block_patterns', 100 ); - /** * Exposes the site logo URL through the WordPress REST API. * diff --git a/lib/compat/wordpress-6.2/block-patterns.php b/lib/compat/wordpress-6.2/block-patterns.php index 8f50c9ff33c5d6..1e529faf963210 100644 --- a/lib/compat/wordpress-6.2/block-patterns.php +++ b/lib/compat/wordpress-6.2/block-patterns.php @@ -8,7 +8,7 @@ /** * Registers the block pattern categories. */ -function gutenberg_register_core_block_patterns_and_categories() { +function gutenberg_register_core_block_patterns_categories() { register_block_pattern_category( 'banner', array( @@ -30,49 +30,115 @@ function gutenberg_register_core_block_patterns_and_categories() { ) ); register_block_pattern_category( - 'footer', + 'text', array( - 'label' => _x( 'Footers', 'Block pattern category', 'gutenberg' ), - 'description' => __( 'A variety of footer designs displaying information and site navigation.', 'gutenberg' ), + 'label' => _x( 'Text', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Patterns containing mostly text.', 'gutenberg' ), ) ); register_block_pattern_category( - 'gallery', + 'query', array( - 'label' => _x( 'Gallery', 'Block pattern category', 'gutenberg' ), - 'description' => __( 'Patterns containing mostly images or other media.', 'gutenberg' ), + 'label' => _x( 'Posts', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Display your latest posts in lists, grids or other layouts.', 'gutenberg' ), ) ); register_block_pattern_category( - 'header', + 'featured', array( - 'label' => _x( 'Headers', 'Block pattern category', 'gutenberg' ), - 'description' => __( 'A variety of header designs displaying your site title and navigation.', 'gutenberg' ), + 'label' => _x( 'Featured', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'A set of high quality curated patterns.', 'gutenberg' ), ) ); + + // Register new core block pattern categories. register_block_pattern_category( - 'text', + 'call-to-action', array( - 'label' => _x( 'Text', 'Block pattern category', 'gutenberg' ), - 'description' => __( 'Patterns containing mostly text.', 'gutenberg' ), + 'label' => _x( 'Call to Action', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Sections whose purpose is to trigger a specific action.', 'gutenberg' ), ) ); register_block_pattern_category( - 'query', + 'team', + array( + 'label' => _x( 'Team', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'A variety of designs to display your team members.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'testimonials', + array( + 'label' => _x( 'Testimonials', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Share reviews and feedback about your brand/business.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'services', + array( + 'label' => _x( 'Services', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Briefly describe what your business does and how you can help.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'contact', + array( + 'label' => _x( 'Contact', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Display your contact information.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'about', + array( + 'label' => _x( 'About', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Introduce yourself.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'portfolio', + array( + 'label' => _x( 'Portfolio', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Showcase your latest work.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'gallery', + array( + 'label' => _x( 'Gallery', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Different layouts for displaying images.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'media', + array( + 'label' => _x( 'Media', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'Different layouts containing video or audio.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'posts', array( 'label' => _x( 'Posts', 'Block pattern category', 'gutenberg' ), 'description' => __( 'Display your latest posts in lists, grids or other layouts.', 'gutenberg' ), ) ); + // Site building pattern categories. register_block_pattern_category( - 'featured', + 'footer', array( - 'label' => _x( 'Featured', 'Block pattern category', 'gutenberg' ), - 'description' => __( 'A set of high quality curated patterns.', 'gutenberg' ), + 'label' => _x( 'Footers', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'A variety of footer designs displaying information and site navigation.', 'gutenberg' ), + ) + ); + register_block_pattern_category( + 'header', + array( + 'label' => _x( 'Headers', 'Block pattern category', 'gutenberg' ), + 'description' => __( 'A variety of header designs displaying your site title and navigation.', 'gutenberg' ), ) ); } -add_action( 'init', 'gutenberg_register_core_block_patterns_and_categories' ); +add_action( 'init', 'gutenberg_register_core_block_patterns_categories' ); /** * Registers Gutenberg-bundled patterns, with a focus on headers and footers diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php new file mode 100644 index 00000000000000..d34160edb2af0b --- /dev/null +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php @@ -0,0 +1,107 @@ + 'call-to-action', + 'columns' => 'text', + 'query' => 'posts', + ); + + /** + * Registers the routes for the objects of the controller. + * + * @since 6.0.0 + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ), + true + ); + } + /** + * Retrieves all block patterns. + * + * @since 6.0.0 + * @since 6.2.0 Added migration for old core pattern categories to the new ones. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function get_items( $request ) { + if ( ! $this->remote_patterns_loaded ) { + // Load block patterns from w.org. + _load_remote_block_patterns(); // Patterns with the `core` keyword. + _load_remote_featured_patterns(); // Patterns in the `featured` category. + _register_remote_theme_patterns(); // Patterns requested by current theme. + + $this->remote_patterns_loaded = true; + } + + $response = array(); + $patterns = WP_Block_Patterns_Registry::get_instance()->get_all_registered(); + foreach ( $patterns as $pattern ) { + $migrated_pattern = $this->migrate_pattern_categories( $pattern ); + $prepared_pattern = $this->prepare_item_for_response( $migrated_pattern, $request ); + $response[] = $this->prepare_response_for_collection( $prepared_pattern ); + } + return rest_ensure_response( $response ); + } + + /** + * Migrates old core pattern categories to new ones. + * + * Core pattern categories are being revamped and we need to handle the migration + * to the new ones and ensure backwards compatibility. + * + * @since 6.2.0 + * + * @param array $pattern Raw pattern as registered, before applying any changes. + * @return array Migrated pattern. + */ + protected function migrate_pattern_categories( $pattern ) { + if ( isset( $pattern['categories'] ) && is_array( $pattern['categories'] ) ) { + foreach ( $pattern['categories'] as $i => $category ) { + if ( array_key_exists( $category, static::$categories_migration ) ) { + $pattern['categories'][ $i ] = static::$categories_migration[ $category ]; + } + } + } + return $pattern; + } +} diff --git a/lib/compat/wordpress-6.2/rest-api.php b/lib/compat/wordpress-6.2/rest-api.php index 023a79e3db95fb..4541ce0ae158c6 100644 --- a/lib/compat/wordpress-6.2/rest-api.php +++ b/lib/compat/wordpress-6.2/rest-api.php @@ -23,6 +23,15 @@ function gutenberg_register_rest_pattern_directory() { } add_action( 'rest_api_init', 'gutenberg_register_rest_pattern_directory' ); +/** + * Registers the block patterns REST API routes. + */ +function gutenberg_register_rest_block_patterns() { + $block_patterns = new Gutenberg_REST_Block_Patterns_Controller_6_2(); + $block_patterns->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_rest_block_patterns' ); + /** * Add extra collection params to pattern directory requests. * diff --git a/lib/load.php b/lib/load.php index 3c9f458fcd3881..d2ec3611e6cb42 100644 --- a/lib/load.php +++ b/lib/load.php @@ -36,11 +36,12 @@ function gutenberg_is_experiment_enabled( $name ) { } // WordPress 6.1 compat. - require_once __DIR__ . '/compat/wordpress-6.1/class-gutenberg-rest-block-patterns-controller.php'; + require_once __DIR__ . '/compat/wordpress-6.1/class-gutenberg-rest-block-patterns-controller-6-1.php'; require_once __DIR__ . '/compat/wordpress-6.1/class-gutenberg-rest-templates-controller.php'; require_once __DIR__ . '/compat/wordpress-6.1/rest-api.php'; // WordPress 6.2 compat. + require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-block-patterns-controller-6-2.php'; require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-block-pattern-categories-controller.php'; require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php'; require_once __DIR__ . '/compat/wordpress-6.2/rest-api.php'; diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab.js b/packages/block-editor/src/components/inserter/block-patterns-tab.js index a1e9c839894650..93b4ca40978e19 100644 --- a/packages/block-editor/src/components/inserter/block-patterns-tab.js +++ b/packages/block-editor/src/components/inserter/block-patterns-tab.js @@ -53,10 +53,17 @@ function usePatternsCategories() { ) ) .sort( ( { name: currentName }, { name: nextName } ) => { - if ( ! [ currentName, nextName ].includes( 'featured' ) ) { + if ( + ! [ currentName, nextName ].some( ( categoryName ) => + [ 'featured', 'text' ].includes( categoryName ) + ) + ) { return 0; } - return currentName === 'featured' ? -1 : 1; + // Move `featured` category to the top and `text` to the bottom. + return currentName === 'featured' || nextName === 'text' + ? -1 + : 1; } ); if ( diff --git a/phpunit/class-gutenberg-rest-block-patterns-controller-test.php b/phpunit/class-gutenberg-rest-block-patterns-controller-test.php index ad6f42b033e84b..9f9cf60f0572a6 100644 --- a/phpunit/class-gutenberg-rest-block-patterns-controller-test.php +++ b/phpunit/class-gutenberg-rest-block-patterns-controller-test.php @@ -10,27 +10,54 @@ * Unit tests for REST API for Block Patterns. * * @group restapi - * @covers Gutenberg_REST_Block_Patterns_Controller + * @covers Gutenberg_REST_Block_Patterns_Controller_6_2 */ -class Gutenberg_REST_Block_Patterns_Controller_Test extends WP_Test_REST_Controller_Testcase { +class Gutenberg_REST_Block_Patterns_Controller_6_2_Test extends WP_Test_REST_Controller_Testcase { + /** + * Admin user ID. + * + * @since 6.0.0 + * + * @var int + */ protected static $admin_id; + + /** + * Original instance of WP_Block_Patterns_Registry. + * + * @since 6.0.0 + * + * @var WP_Block_Patterns_Registry + */ protected static $orig_registry; - public function set_up() { - parent::set_up(); - switch_theme( 'emptytheme' ); - } + /** + * Instance of the reflected `instance` property. + * + * @since 6.0.0 + * + * @var ReflectionProperty + */ + private static $registry_instance_property; + + /** + * The REST API route. + * + * @since 6.0.0 + * + * @var string + */ + const REQUEST_ROUTE = '/wp/v2/block-patterns/patterns'; public static function wpSetUpBeforeClass( $factory ) { - // Create a test user. self::$admin_id = $factory->user->create( array( 'role' => 'administrator' ) ); // Setup an empty testing instance of `WP_Block_Patterns_Registry` and save the original. - $reflection = new ReflectionClass( 'WP_Block_Patterns_Registry' ); - $reflection->getProperty( 'instance' )->setAccessible( true ); - self::$orig_registry = $reflection->getStaticPropertyValue( 'instance' ); - $test_registry = new WP_Block_Patterns_Registry(); - $reflection->setStaticPropertyValue( 'instance', $test_registry ); + self::$orig_registry = WP_Block_Patterns_Registry::get_instance(); + self::$registry_instance_property = new ReflectionProperty( 'WP_Block_Patterns_Registry', 'instance' ); + self::$registry_instance_property->setAccessible( true ); + $test_registry = new WP_Block_Pattern_Categories_Registry(); + self::$registry_instance_property->setValue( $test_registry ); // Register some patterns in the test registry. $test_registry->register( @@ -51,47 +78,77 @@ public static function wpSetUpBeforeClass( $factory ) { 'content' => '

Two

', ) ); + + $test_registry->register( + 'test/three', + array( + 'title' => 'Pattern Three', + 'categories' => array( 'test', 'buttons', 'query' ), + 'content' => '

Three

', + ) + ); } public static function wpTearDownAfterClass() { - // Delete the test user. self::delete_user( self::$admin_id ); // Restore the original registry instance. - $reflection = new ReflectionClass( 'WP_Block_Patterns_Registry' ); - $reflection->setStaticPropertyValue( 'instance', self::$orig_registry ); + self::$registry_instance_property->setValue( self::$orig_registry ); + self::$registry_instance_property->setAccessible( false ); + self::$registry_instance_property = null; + self::$orig_registry = null; } - public function test_register_routes() { - $routes = rest_get_server()->get_routes(); - $this->assertArrayHasKey( - '/wp/v2/block-patterns/patterns', - $routes, - 'The patterns route does not exist' - ); + public function set_up() { + parent::set_up(); + switch_theme( 'emptytheme' ); } - public function test_get_items() { + public function test_get_items_migrate_pattern_categories() { wp_set_current_user( self::$admin_id ); - $expected_names = array( 'test/one', 'test/two' ); - $expected_fields = array( 'name', 'content' ); - - $request = new WP_REST_Request( 'GET', '/wp/v2/block-patterns/patterns' ); - $request['_fields'] = 'name,content'; + $request = new WP_REST_Request( 'GET', static::REQUEST_ROUTE ); + $request['_fields'] = 'name,categories'; $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); - $this->assertCount( count( $expected_names ), $data ); - foreach ( $data as $idx => $item ) { - $this->assertEquals( $expected_names[ $idx ], $item['name'] ); - $this->assertEquals( $expected_fields, array_keys( $item ) ); - } + $this->assertIsArray( $data, 'WP_REST_Block_Patterns_Controller::get_items() should return an array' ); + $this->assertGreaterThanOrEqual( 3, count( $data ), 'WP_REST_Block_Patterns_Controller::get_items() should return at least 3 items' ); + $this->assertSame( + array( + 'name' => 'test/one', + 'categories' => array( 'test' ), + ), + $data[0], + 'WP_REST_Block_Patterns_Controller::get_items() should return test/one' + ); + $this->assertSame( + array( + 'name' => 'test/two', + 'categories' => array( 'test' ), + ), + $data[1], + 'WP_REST_Block_Patterns_Controller::get_items() should return test/two' + ); + $this->assertSame( + array( + 'name' => 'test/three', + 'categories' => array( 'test', 'call-to-action', 'posts' ), + ), + $data[2], + 'WP_REST_Block_Patterns_Controller::get_items() should return test/three' + ); } /** * Abstract methods that we must implement. */ + public function test_register_routes() { + $this->markTestIncomplete(); + } + public function test_get_items() { + $this->markTestIncomplete(); + } public function test_context_param() { $this->markTestIncomplete(); } From b1202138f4e44fa6312e93f2e7d5132cf73586cd Mon Sep 17 00:00:00 2001 From: Corentin Gautier <4660731+corentin-gautier@users.noreply.github.com> Date: Mon, 12 Dec 2022 12:51:00 +0100 Subject: [PATCH 04/73] Avoid paint on popovers when scrolling (#46187) * Avoid paint on popovers * fix e2e test Co-authored-by: Corentin Gautier --- .../test/__snapshots__/index.js.snap | 6 +- packages/components/CHANGELOG.md | 2 + packages/components/src/popover/index.tsx | 11 +- packages/components/src/popover/style.scss | 1 + .../dot-tip/test/__snapshots__/index.js.snap | 2 +- .../e2e/specs/editor/blocks/paragraph.spec.js | 122 ++++++++++++++---- 6 files changed, 110 insertions(+), 34 deletions(-) diff --git a/packages/block-editor/src/components/url-popover/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/url-popover/test/__snapshots__/index.js.snap index 12c09f2c7c98ab..c7ecbc209a2417 100644 --- a/packages/block-editor/src/components/url-popover/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/url-popover/test/__snapshots__/index.js.snap @@ -5,7 +5,7 @@ exports[`URLPopover matches the snapshot in its default state 1`] = `
diff --git a/packages/components/src/popover/style.scss b/packages/components/src/popover/style.scss index d595c23ae274a9..790b840e6a648b 100644 --- a/packages/components/src/popover/style.scss +++ b/packages/components/src/popover/style.scss @@ -7,6 +7,7 @@ $shadow-popover-border-top-only-alternate: 0 #{-$border-width} 0 $gray-900; .components-popover { z-index: z-index(".components-popover"); + will-change: transform; &.is-expanded { position: fixed; diff --git a/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap b/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap index 876bae4b690887..a453da0254b487 100644 --- a/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap +++ b/packages/nux/src/components/dot-tip/test/__snapshots__/index.js.snap @@ -5,7 +5,7 @@ exports[`DotTip should render correctly 1`] = ` aria-label="Editor tips" class="components-popover nux-dot-tip is-positioned" role="dialog" - style="position: absolute; opacity: 0; transform: translateX(-2em) scale(0) translateZ(0); transform-origin: 0% 50% 0; left: 0px; top: 0px;" + style="position: absolute; top: 0px; left: 0px; opacity: 0; transform: translateX(-2em) scale(0) translateZ(0); transform-origin: 0% 50% 0;" tabindex="-1" >
{ ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( firstBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + firstBlockBox + ) + ) + .toBeTruthy(); } { @@ -207,8 +211,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( firstBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + firstBlockBox + ) + ) + .toBeTruthy(); } { @@ -219,8 +227,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( firstBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + firstBlockBox + ) + ) + .toBeTruthy(); } { @@ -231,8 +243,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( firstBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + firstBlockBox + ) + ) + .toBeTruthy(); } { @@ -309,8 +325,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( secondBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + secondBlockBox + ) + ) + .toBeTruthy(); } { @@ -321,8 +341,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( secondBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + secondBlockBox + ) + ) + .toBeTruthy(); } { @@ -333,8 +357,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( secondBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + secondBlockBox + ) + ) + .toBeTruthy(); } { @@ -345,8 +373,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( secondBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + secondBlockBox + ) + ) + .toBeTruthy(); } } ); @@ -387,8 +419,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( firstBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + firstBlockBox + ) + ) + .toBeTruthy(); } { @@ -399,8 +435,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( firstBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + firstBlockBox + ) + ) + .toBeTruthy(); } { @@ -411,8 +451,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( firstBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + firstBlockBox + ) + ) + .toBeTruthy(); } { @@ -423,8 +467,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( secondBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + secondBlockBox + ) + ) + .toBeTruthy(); } { @@ -435,8 +483,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( secondBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + secondBlockBox + ) + ) + .toBeTruthy(); } { @@ -447,8 +499,12 @@ test.describe( 'Paragraph', () => { ); await expect( draggingUtils.dropZone ).toBeVisible(); await expect - .poll( () => draggingUtils.dropZone.boundingBox() ) - .toEqual( secondBlockBox ); + .poll( () => + draggingUtils.confirmValidDropZonePosition( + secondBlockBox + ) + ) + .toBeTruthy(); } } ); } ); @@ -508,4 +564,14 @@ class DraggingUtils { await this.page.mouse.move( 0, 0 ); await this.page.mouse.down(); } + + async confirmValidDropZonePosition( element ) { + // Check that both x and y axis of the dropzone + // have a less than 1 difference with a given target element + const box = await this.dropZone.boundingBox(); + return ( + Math.abs( element.x - box.x ) < 1 && + Math.abs( element.y - box.y ) < 1 + ); + } } From 78deee07cef231fd0ea3ce08ff464b6fd08b438e Mon Sep 17 00:00:00 2001 From: Maggie Date: Mon, 12 Dec 2022 13:05:24 +0100 Subject: [PATCH 05/73] removed the edit button cell when it's not editable (#46439) --- .../src/components/off-canvas-editor/block.js | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/src/components/off-canvas-editor/block.js b/packages/block-editor/src/components/off-canvas-editor/block.js index c88f5ed0f7bbff..af74873e1b41af 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block.js +++ b/packages/block-editor/src/components/off-canvas-editor/block.js @@ -325,27 +325,28 @@ function ListViewBlock( { { showBlockActions && ( <> - - { ( props ) => - isEditable && ( + { isEditable && ( + + { ( props ) => ( - ) - } - + ) } + + ) } { ( { ref, tabIndex, onFocus } ) => ( Date: Mon, 12 Dec 2022 13:41:52 +0100 Subject: [PATCH 06/73] Allow adding new templates and template parts directly from the sidebar (#46458) --- .../src/components/add-new-template/index.js | 9 +++-- .../add-new-template/new-template-part.js | 13 +++++-- .../add-new-template/new-template.js | 15 +++++--- .../components/add-new-template/style.scss | 4 -- .../src/components/layout/style.scss | 3 +- .../edit-site/src/components/list/header.js | 6 ++- .../sidebar-navigation-screen-main/index.js | 2 +- .../index.js | 38 +++++++++++++------ .../style.scss | 5 +++ .../sidebar-navigation-screen/style.scss | 6 ++- 10 files changed, 68 insertions(+), 33 deletions(-) diff --git a/packages/edit-site/src/components/add-new-template/index.js b/packages/edit-site/src/components/add-new-template/index.js index 89abbc0aa4614c..c3ba06f065bb99 100644 --- a/packages/edit-site/src/components/add-new-template/index.js +++ b/packages/edit-site/src/components/add-new-template/index.js @@ -10,7 +10,10 @@ import { store as coreStore } from '@wordpress/core-data'; import NewTemplate from './new-template'; import NewTemplatePart from './new-template-part'; -export default function AddNewTemplate( { templateType = 'wp_template' } ) { +export default function AddNewTemplate( { + templateType = 'wp_template', + ...props +} ) { const postType = useSelect( ( select ) => select( coreStore ).getPostType( templateType ), [ templateType ] @@ -21,9 +24,9 @@ export default function AddNewTemplate( { templateType = 'wp_template' } ) { } if ( templateType === 'wp_template' ) { - return ; + return ; } else if ( templateType === 'wp_template_part' ) { - return ; + return ; } return null; diff --git a/packages/edit-site/src/components/add-new-template/new-template-part.js b/packages/edit-site/src/components/add-new-template/new-template-part.js index 783545bac826b7..578cfa515bd2c2 100644 --- a/packages/edit-site/src/components/add-new-template/new-template-part.js +++ b/packages/edit-site/src/components/add-new-template/new-template-part.js @@ -12,6 +12,7 @@ import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; +import { plus } from '@wordpress/icons'; /** * Internal dependencies @@ -20,7 +21,11 @@ import { useHistory } from '../routes'; import { store as editSiteStore } from '../../store'; import CreateTemplatePartModal from '../create-template-part-modal'; -export default function NewTemplatePart( { postType } ) { +export default function NewTemplatePart( { + postType, + showIcon = true, + toggleProps, +} ) { const history = useHistory(); const [ isModalOpen, setIsModalOpen ] = useState( false ); const { createErrorNotice } = useDispatch( noticesStore ); @@ -83,12 +88,14 @@ export default function NewTemplatePart( { postType } ) { return ( <> { isModalOpen && ( { () => ( <> diff --git a/packages/edit-site/src/components/add-new-template/style.scss b/packages/edit-site/src/components/add-new-template/style.scss index 51fb9af347c2ce..f247bfa214cfb6 100644 --- a/packages/edit-site/src/components/add-new-template/style.scss +++ b/packages/edit-site/src/components/add-new-template/style.scss @@ -1,8 +1,4 @@ .edit-site-new-template-dropdown { - .components-dropdown-menu__toggle { - padding: 6px 12px; - } - .edit-site-new-template-dropdown__popover { @include break-small() { min-width: 300px; diff --git a/packages/edit-site/src/components/layout/style.scss b/packages/edit-site/src/components/layout/style.scss index de34bc008f739b..4e92e6c0a57fb8 100644 --- a/packages/edit-site/src/components/layout/style.scss +++ b/packages/edit-site/src/components/layout/style.scss @@ -39,7 +39,8 @@ .edit-site-layout__edit-button { background: $gray-800; - color: $white; + /* Overrides the color for all states of the button */ + color: inherit !important; } .edit-site-layout__sidebar { diff --git a/packages/edit-site/src/components/list/header.js b/packages/edit-site/src/components/list/header.js index 91b9a13aeeb9c7..5468c271fe628d 100644 --- a/packages/edit-site/src/components/list/header.js +++ b/packages/edit-site/src/components/list/header.js @@ -36,7 +36,11 @@ export default function Header( { templateType } ) { { canCreate && (
- +
) } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js index 9a7b4479c972bc..622eb70a951aa0 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js @@ -32,7 +32,7 @@ export default function SidebarNavigationScreenMain() { +
{ __( 'Design' ) }
{ ! isMobileViewport && isEditorPage && ( + +
+ { config[ postType ].labels.title } +
+ { ! isMobileViewport && ( + + + { isEditorPage && ( + + ) } + ) }
} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-templates/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-templates/style.scss index 432774f903e465..174c7210e8403d 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-templates/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen-templates/style.scss @@ -2,3 +2,8 @@ /* Overrides the margin that comes from the Item component */ margin-top: $grid-unit-20 !important; } + +.edit-site-sidebar-navigation-screen-templates__add-button { + /* Overrides the color for all states of the button */ + color: inherit !important; +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss index e3044e95bbdd05..91c64bbd4c3ad2 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss +++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss @@ -6,7 +6,7 @@ } .edit-site-sidebar-navigation-screen__content { - margin: 0 $button-size; + margin: 0 $grid-unit-20 0 $button-size; flex-grow: 1; overflow-y: auto; } @@ -19,11 +19,13 @@ box-shadow: 0 $grid-unit-10 $grid-unit-20 $gray-900; margin-bottom: $grid-unit-10; padding-bottom: $grid-unit-10; + padding-right: $grid-unit-20; } .edit-site-sidebar-navigation-screen__title { font-size: calc(1.56 * 13px); - font-weight: 600; + font-weight: 500; + flex-grow: 1; } .edit-site-sidebar-navigation-screen__back { From 2fc3da7ffdf8f990d2c685bd22cf53adb60ff5d4 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 12 Dec 2022 23:58:18 +0900 Subject: [PATCH 07/73] Shortcuts: Add Ctrl+Y for redo to all editor instances on Windows (#43392) --- .../src/components/keyboard-shortcuts/index.js | 13 +++++++++++++ .../src/components/layout/shortcuts.js | 13 +++++++++++++ .../src/components/keyboard-shortcuts/index.js | 13 +++++++++++++ .../src/components/keyboard-shortcuts/index.js | 13 +++++++++++++ 4 files changed, 52 insertions(+) diff --git a/packages/customize-widgets/src/components/keyboard-shortcuts/index.js b/packages/customize-widgets/src/components/keyboard-shortcuts/index.js index 56888f1cfe3ae5..5c5f3ea2bca832 100644 --- a/packages/customize-widgets/src/components/keyboard-shortcuts/index.js +++ b/packages/customize-widgets/src/components/keyboard-shortcuts/index.js @@ -6,6 +6,7 @@ import { useShortcut, store as keyboardShortcutsStore, } from '@wordpress/keyboard-shortcuts'; +import { isAppleOS } from '@wordpress/keycodes'; import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; @@ -52,6 +53,18 @@ function KeyboardShortcutsRegister() { modifier: 'primaryShift', character: 'z', }, + // Disable on Apple OS because it conflicts with the browser's + // history shortcut. It's a fine alias for both Windows and Linux. + // Since there's no conflict for Ctrl+Shift+Z on both Windows and + // Linux, we keep it as the default for consistency. + aliases: isAppleOS() + ? [] + : [ + { + modifier: 'primary', + character: 'y', + }, + ], } ); registerShortcut( { diff --git a/packages/edit-navigation/src/components/layout/shortcuts.js b/packages/edit-navigation/src/components/layout/shortcuts.js index 1ced3ba2183fee..8036d0534261e9 100644 --- a/packages/edit-navigation/src/components/layout/shortcuts.js +++ b/packages/edit-navigation/src/components/layout/shortcuts.js @@ -7,6 +7,7 @@ import { useShortcut, store as keyboardShortcutsStore, } from '@wordpress/keyboard-shortcuts'; +import { isAppleOS } from '@wordpress/keycodes'; import { __ } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; @@ -59,6 +60,18 @@ function RegisterNavigationEditorShortcuts() { modifier: 'primaryShift', character: 'z', }, + // Disable on Apple OS because it conflicts with the browser's + // history shortcut. It's a fine alias for both Windows and Linux. + // Since there's no conflict for Ctrl+Shift+Z on both Windows and + // Linux, we keep it as the default for consistency. + aliases: isAppleOS() + ? [] + : [ + { + modifier: 'primary', + character: 'y', + }, + ], } ); }, [ registerShortcut ] ); diff --git a/packages/edit-site/src/components/keyboard-shortcuts/index.js b/packages/edit-site/src/components/keyboard-shortcuts/index.js index 96255dd928c600..8d4337dd097c51 100644 --- a/packages/edit-site/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-site/src/components/keyboard-shortcuts/index.js @@ -6,6 +6,7 @@ import { useShortcut, store as keyboardShortcutsStore, } from '@wordpress/keyboard-shortcuts'; +import { isAppleOS } from '@wordpress/keycodes'; import { useDispatch, useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; @@ -119,6 +120,18 @@ function KeyboardShortcutsRegister() { modifier: 'primaryShift', character: 'z', }, + // Disable on Apple OS because it conflicts with the browser's + // history shortcut. It's a fine alias for both Windows and Linux. + // Since there's no conflict for Ctrl+Shift+Z on both Windows and + // Linux, we keep it as the default for consistency. + aliases: isAppleOS() + ? [] + : [ + { + modifier: 'primary', + character: 'y', + }, + ], } ); registerShortcut( { diff --git a/packages/edit-widgets/src/components/keyboard-shortcuts/index.js b/packages/edit-widgets/src/components/keyboard-shortcuts/index.js index dd039295c3661e..65ecceeb4aa8c1 100644 --- a/packages/edit-widgets/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-widgets/src/components/keyboard-shortcuts/index.js @@ -6,6 +6,7 @@ import { useShortcut, store as keyboardShortcutsStore, } from '@wordpress/keyboard-shortcuts'; +import { isAppleOS } from '@wordpress/keycodes'; import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { store as coreStore } from '@wordpress/core-data'; @@ -59,6 +60,18 @@ function KeyboardShortcutsRegister() { modifier: 'primaryShift', character: 'z', }, + // Disable on Apple OS because it conflicts with the browser's + // history shortcut. It's a fine alias for both Windows and Linux. + // Since there's no conflict for Ctrl+Shift+Z on both Windows and + // Linux, we keep it as the default for consistency. + aliases: isAppleOS() + ? [] + : [ + { + modifier: 'primary', + character: 'y', + }, + ], } ); registerShortcut( { From 403b40fb51d1ec2abe2e92cd571ea9e3f6c6bf7b Mon Sep 17 00:00:00 2001 From: Jorge Date: Mon, 12 Dec 2022 16:27:25 +0000 Subject: [PATCH 08/73] Fix bug on multi-block-selection.test.js. --- .../specs/editor/various/multi-block-selection.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js b/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js index 3d0db13db8ad02..6afe91c6e9257e 100644 --- a/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js +++ b/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js @@ -70,7 +70,7 @@ async function testNativeSelection() { return; } - if ( ! selection.rangeCount === 1 ) { + if ( selection.rangeCount !== 1 ) { throw 'expected one range'; } From 8ddafa1582518723277adc14b97d06859f63d1b5 Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Tue, 13 Dec 2022 10:08:27 +1100 Subject: [PATCH 09/73] Global styles: Allow indirect properties when unfiltered_html is not allowed (#46388) * Global styles: Allow inferred properties outside of compute_style_properties when unfiltered_html is not allowed * Ensure set_current_user callback is covered * Add a simple test * Renamed to indirect, move hooks to separate kses file * Add block-level styles to the test --- .../wordpress-6.2/class-wp-theme-json-6-2.php | 61 +++++++++++++++++ lib/experimental/kses.php | 66 +++++++++++++++++++ lib/load.php | 1 + phpunit/class-wp-theme-json-test.php | 44 +++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 lib/experimental/kses.php diff --git a/lib/compat/wordpress-6.2/class-wp-theme-json-6-2.php b/lib/compat/wordpress-6.2/class-wp-theme-json-6-2.php index 4c6a1f50612a84..3b368a10b7f303 100644 --- a/lib/compat/wordpress-6.2/class-wp-theme-json-6-2.php +++ b/lib/compat/wordpress-6.2/class-wp-theme-json-6-2.php @@ -97,6 +97,25 @@ class WP_Theme_JSON_6_2 extends WP_Theme_JSON_6_1 { 'box-shadow' => array( 'shadow' ), ); + /** + * Indirect metadata for style properties that are not directly output. + * + * Each element is a direct mapping from a CSS property name to the + * path to the value in theme.json & block attributes. + * + * Indirect properties are not output directly by `compute_style_properties`, + * but are used elsewhere in the processing of global styles. The indirect + * property is used to validate whether or not a style value is allowed. + * + * @since 6.2.0 + * @var array + */ + const INDIRECT_PROPERTIES_METADATA = array( + 'gap' => array( 'spacing', 'blockGap' ), + 'column-gap' => array( 'spacing', 'blockGap', 'left' ), + 'row-gap' => array( 'spacing', 'blockGap', 'top' ), + ); + /** * The valid properties under the settings key. * @@ -216,4 +235,46 @@ class WP_Theme_JSON_6_2 extends WP_Theme_JSON_6_1 { 'textTransform' => null, ), ); + + /** + * Processes a style node and returns the same node + * without the insecure styles. + * + * @since 5.9.0 + * @since 6.2.0 Allow indirect properties used outside of `compute_style_properties`. + * + * @param array $input Node to process. + * @return array + */ + protected static function remove_insecure_styles( $input ) { + $output = array(); + $declarations = static::compute_style_properties( $input ); + + foreach ( $declarations as $declaration ) { + if ( static::is_safe_css_declaration( $declaration['name'], $declaration['value'] ) ) { + $path = static::PROPERTIES_METADATA[ $declaration['name'] ]; + + // Check the value isn't an array before adding so as to not + // double up shorthand and longhand styles. + $value = _wp_array_get( $input, $path, array() ); + if ( ! is_array( $value ) ) { + _wp_array_set( $output, $path, $value ); + } + } + } + + // Ensure indirect properties not handled by `compute_style_properties` are allowed. + foreach ( static::INDIRECT_PROPERTIES_METADATA as $property => $path ) { + $value = _wp_array_get( $input, $path, array() ); + if ( + isset( $value ) && + ! is_array( $value ) && + static::is_safe_css_declaration( $property, $value ) + ) { + _wp_array_set( $output, $path, $value ); + } + } + + return $output; + } } diff --git a/lib/experimental/kses.php b/lib/experimental/kses.php new file mode 100644 index 00000000000000..fd7531617939fc --- /dev/null +++ b/lib/experimental/kses.php @@ -0,0 +1,66 @@ +assertEquals( $expected, $theme_json->get_stylesheet() ); } + public function test_allow_indirect_properties() { + $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/social-links' => array( + 'spacing' => array( + 'blockGap' => array( + 'top' => '1em', + 'left' => '2em', + ), + ), + ), + ), + 'spacing' => array( + 'blockGap' => '3em', + ), + ), + ) + ); + + $expected = array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/social-links' => array( + 'spacing' => array( + 'blockGap' => array( + 'top' => '1em', + 'left' => '2em', + ), + ), + ), + ), + 'spacing' => array( + 'blockGap' => '3em', + ), + ), + ); + + $this->assertEqualSetsWithIndex( $expected, $actual ); + } + public function test_remove_invalid_element_pseudo_selectors() { $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties( array( From a7dcc68b3b0831b115faadf177f1d8eba5412a6c Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:53:23 +1100 Subject: [PATCH 10/73] Global Styles: Fix Reset to defaults action by moving fills to be within context provider (#46486) --- packages/edit-site/src/components/editor/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 2bb06063bc75aa..b9ce354f58159b 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -172,7 +172,6 @@ export default function Editor() { <> { isEditMode && } - + Date: Tue, 13 Dec 2022 13:17:21 +0900 Subject: [PATCH 11/73] Modal: Fix unexpected modal closing in IME Composition (#46453) * Modal: Fix unexpected modal Closing in IME Compositions * Add a unit test --- packages/components/CHANGELOG.md | 1 + packages/components/src/modal/index.tsx | 11 +++++++++++ packages/components/src/modal/stories/index.tsx | 3 +++ packages/components/src/modal/test/index.tsx | 15 +++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 21d968568374b4..7ed9fc6779bbae 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -16,6 +16,7 @@ - `ColorPalette`: show "Clear" button even when colors array is empty ([#46001](https://github.com/WordPress/gutenberg/pull/46001)). - `InputControl`: Fix internal `Flex` wrapper usage that could add an unintended `height: 100%` ([#46213](https://github.com/WordPress/gutenberg/pull/46213)). - `Navigator`: Allow calling `goTo` and `goBack` twice in one render cycle ([#46391](https://github.com/WordPress/gutenberg/pull/46391)). +- `Modal`: Fix unexpected modal closing in IME Composition ([#46453](https://github.com/WordPress/gutenberg/pull/46453)). ### Enhancements diff --git a/packages/components/src/modal/index.tsx b/packages/components/src/modal/index.tsx index 8c4f3a8535704d..10316b56993c40 100644 --- a/packages/components/src/modal/index.tsx +++ b/packages/components/src/modal/index.tsx @@ -98,6 +98,17 @@ function UnforwardedModal( }, [ bodyOpenClassName ] ); function handleEscapeKeyDown( event: KeyboardEvent< HTMLDivElement > ) { + if ( + // Ignore keydowns from IMEs + event.nativeEvent.isComposing || + // Workaround for Mac Safari where the final Enter/Backspace of an IME composition + // is `isComposing=false`, even though it's technically still part of the composition. + // These can only be detected by keyCode. + event.keyCode === 229 + ) { + return; + } + if ( shouldCloseOnEsc && event.code === 'Escape' && diff --git a/packages/components/src/modal/stories/index.tsx b/packages/components/src/modal/stories/index.tsx index 42b7874b1eeaab..7f414d47c2d11c 100644 --- a/packages/components/src/modal/stories/index.tsx +++ b/packages/components/src/modal/stories/index.tsx @@ -12,6 +12,7 @@ import { useState } from '@wordpress/element'; * Internal dependencies */ import Button from '../../button'; +import InputControl from '../../input-control'; import Modal from '../'; import type { ModalProps } from '../types'; @@ -75,6 +76,8 @@ const Template: ComponentStory< typeof Modal > = ( { anim id est laborum.

+ + diff --git a/packages/components/src/modal/test/index.tsx b/packages/components/src/modal/test/index.tsx index a0778bfb8aab8b..310dc3d6e63a9f 100644 --- a/packages/components/src/modal/test/index.tsx +++ b/packages/components/src/modal/test/index.tsx @@ -2,6 +2,7 @@ * External dependencies */ import { render, screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; /** * Internal dependencies @@ -69,4 +70,18 @@ describe( 'Modal', () => { const title = within( dialog ).queryByText( 'Test Title' ); expect( title ).not.toBeInTheDocument(); } ); + + it( 'should call onRequestClose when the escape key is pressed', async () => { + const user = userEvent.setup( { + advanceTimers: jest.advanceTimersByTime, + } ); + const onRequestClose = jest.fn(); + render( + +

Modal content

+
+ ); + await user.keyboard( '[Escape]' ); + expect( onRequestClose ).toHaveBeenCalled(); + } ); } ); From 436666dc353d3b373a1a587080af94a5f047233e Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Tue, 13 Dec 2022 17:50:26 +1300 Subject: [PATCH 12/73] Global styles: add custom CSS panel to site editor (#46141) --- .../wordpress-6.2/block-editor-settings.php | 28 +++ ...berg-rest-global-styles-controller-6-2.php | 202 ++++++++++++++++++ .../wordpress-6.2/class-wp-theme-json-6-2.php | 111 ++++++++++ .../class-wp-theme-json-resolver-6-2.php | 59 +++++ .../get-global-styles-and-settings.php | 2 +- lib/compat/wordpress-6.2/rest-api.php | 9 + lib/load.php | 4 +- .../components/global-styles/custom-css.js | 73 +++++++ .../src/components/global-styles/hooks.js | 6 +- .../components/global-styles/screen-css.js | 33 +++ .../components/global-styles/screen-root.js | 31 ++- .../src/components/global-styles/style.scss | 15 +- .../src/components/global-styles/ui.js | 4 + .../global-styles/use-global-styles-output.js | 5 + phpunit/class-wp-theme-json-test.php | 14 ++ 15 files changed, 591 insertions(+), 5 deletions(-) create mode 100644 lib/compat/wordpress-6.2/block-editor-settings.php create mode 100644 lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php create mode 100644 packages/edit-site/src/components/global-styles/custom-css.js create mode 100644 packages/edit-site/src/components/global-styles/screen-css.js diff --git a/lib/compat/wordpress-6.2/block-editor-settings.php b/lib/compat/wordpress-6.2/block-editor-settings.php new file mode 100644 index 00000000000000..7323d34eb667ce --- /dev/null +++ b/lib/compat/wordpress-6.2/block-editor-settings.php @@ -0,0 +1,28 @@ + gutenberg_get_global_stylesheet( array( 'custom-css' ) ), + '__unstableType' => 'user', + 'isGlobalStyles' => true, + ); + } + + return $settings; +} + +add_filter( 'block_editor_settings_all', 'gutenberg_get_block_editor_settings_6_2', PHP_INT_MAX ); diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php new file mode 100644 index 00000000000000..9f762dd961d058 --- /dev/null +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php @@ -0,0 +1,202 @@ +post_content, true ); + $is_global_styles_user_theme_json = isset( $raw_config['isGlobalStylesUserThemeJSON'] ) && true === $raw_config['isGlobalStylesUserThemeJSON']; + $config = array(); + if ( $is_global_styles_user_theme_json ) { + $config = ( new WP_Theme_JSON_Gutenberg( $raw_config, 'custom' ) )->get_raw_data(); + } + + // Base fields for every post. + $data = array(); + $fields = $this->get_fields_for_response( $request ); + + if ( rest_is_field_included( 'id', $fields ) ) { + $data['id'] = $post->ID; + } + + if ( rest_is_field_included( 'title', $fields ) ) { + $data['title'] = array(); + } + if ( rest_is_field_included( 'title.raw', $fields ) ) { + $data['title']['raw'] = $post->post_title; + } + if ( rest_is_field_included( 'title.rendered', $fields ) ) { + add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + + $data['title']['rendered'] = get_the_title( $post->ID ); + + remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + } + + if ( rest_is_field_included( 'settings', $fields ) ) { + $data['settings'] = ! empty( $config['settings'] ) && $is_global_styles_user_theme_json ? $config['settings'] : new stdClass(); + } + + if ( rest_is_field_included( 'styles', $fields ) ) { + $data['styles'] = ! empty( $config['styles'] ) && $is_global_styles_user_theme_json ? $config['styles'] : new stdClass(); + } + + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; + $data = $this->add_additional_fields_to_object( $data, $request ); + $data = $this->filter_response_by_context( $data, $context ); + + // Wrap the data in a response object. + $response = rest_ensure_response( $data ); + + if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { + $links = $this->prepare_links( $post->ID ); + $response->add_links( $links ); + if ( ! empty( $links['self']['href'] ) ) { + $actions = $this->get_available_actions(); + $self = $links['self']['href']; + foreach ( $actions as $rel ) { + $response->add_link( $rel, $self ); + } + } + } + + return $response; + } + + /** + * Updates a single global style config. + * + * @since 5.9.0 + * @since 6.2.0 Added validation of styles.css property. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function update_item( $request ) { + $post_before = $this->get_post( $request['id'] ); + if ( is_wp_error( $post_before ) ) { + return $post_before; + } + + $changes = $this->prepare_item_for_database( $request ); + if ( is_wp_error( $changes ) ) { + return $changes; + } + + $result = wp_update_post( wp_slash( (array) $changes ), true, false ); + if ( is_wp_error( $result ) ) { + return $result; + } + + $post = get_post( $request['id'] ); + $fields_update = $this->update_additional_fields_for_object( $post, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } + + wp_after_insert_post( $post, true, $post_before ); + + $response = $this->prepare_item_for_response( $post, $request ); + + return rest_ensure_response( $response ); + } + /** + * Prepares a single global styles config for update. + * + * @since 5.9.0 + * @since 6.2.0 Added validation of styles.css property. + * + * @param WP_REST_Request $request Request object. + * @return stdClass Changes to pass to wp_update_post. + */ + protected function prepare_item_for_database( $request ) { + $changes = new stdClass(); + $changes->ID = $request['id']; + $post = get_post( $request['id'] ); + $existing_config = array(); + if ( $post ) { + $existing_config = json_decode( $post->post_content, true ); + $json_decoding_error = json_last_error(); + if ( JSON_ERROR_NONE !== $json_decoding_error || ! isset( $existing_config['isGlobalStylesUserThemeJSON'] ) || + ! $existing_config['isGlobalStylesUserThemeJSON'] ) { + $existing_config = array(); + } + } + if ( isset( $request['styles'] ) || isset( $request['settings'] ) ) { + $config = array(); + if ( isset( $request['styles'] ) ) { + $config['styles'] = $request['styles']; + $validate_custom_css = $this->validate_custom_css( $request['styles']['css'] ); + if ( is_wp_error( $validate_custom_css ) ) { + return $validate_custom_css; + } + } elseif ( isset( $existing_config['styles'] ) ) { + $config['styles'] = $existing_config['styles']; + } + if ( isset( $request['settings'] ) ) { + $config['settings'] = $request['settings']; + } elseif ( isset( $existing_config['settings'] ) ) { + $config['settings'] = $existing_config['settings']; + } + $config['isGlobalStylesUserThemeJSON'] = true; + $config['version'] = WP_Theme_JSON_Gutenberg::LATEST_SCHEMA; + $changes->post_content = wp_json_encode( $config ); + } + // Post title. + if ( isset( $request['title'] ) ) { + if ( is_string( $request['title'] ) ) { + $changes->post_title = $request['title']; + } elseif ( ! empty( $request['title']['raw'] ) ) { + $changes->post_title = $request['title']['raw']; + } + } + return $changes; + } + + /** + * Validate style.css as valid CSS. + * + * Currently just checks for invalid markup. + * + * @since 6.2.0 + * + * @param string $css CSS to validate. + * @return true|WP_Error True if the input was validated, otherwise WP_Error. + */ + private function validate_custom_css( $css ) { + if ( preg_match( '# 400 ) + ); + } + return true; + } +} diff --git a/lib/compat/wordpress-6.2/class-wp-theme-json-6-2.php b/lib/compat/wordpress-6.2/class-wp-theme-json-6-2.php index 3b368a10b7f303..fe01eb273066e2 100644 --- a/lib/compat/wordpress-6.2/class-wp-theme-json-6-2.php +++ b/lib/compat/wordpress-6.2/class-wp-theme-json-6-2.php @@ -194,6 +194,7 @@ class WP_Theme_JSON_6_2 extends WP_Theme_JSON_6_1 { * @since 6.1.0 Added new side properties for `border`, * added new property `shadow`, * updated `blockGap` to be allowed at any level. + * @since 6.2.0 Added new property `css`. * @var array */ const VALID_STYLES = array( @@ -234,6 +235,7 @@ class WP_Theme_JSON_6_2 extends WP_Theme_JSON_6_1 { 'textDecoration' => null, 'textTransform' => null, ), + 'css' => null, ); /** @@ -277,4 +279,113 @@ protected static function remove_insecure_styles( $input ) { return $output; } + + /** + * Returns the stylesheet that results of processing + * the theme.json structure this object represents. + * + * @param array $types Types of styles to load. Will load all by default. It accepts: + * 'variables': only the CSS Custom Properties for presets & custom ones. + * 'styles': only the styles section in theme.json. + * 'presets': only the classes for the presets. + * 'custom-css': only the css from global styles.css. + * @param array $origins A list of origins to include. By default it includes VALID_ORIGINS. + * @param array $options An array of options for now used for internal purposes only (may change without notice). + * The options currently supported are 'scope' that makes sure all style are scoped to a given selector, + * and root_selector which overwrites and forces a given selector to be used on the root node. + * @return string Stylesheet. + */ + public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = null, $options = array() ) { + if ( null === $origins ) { + $origins = static::VALID_ORIGINS; + } + + if ( is_string( $types ) ) { + // Dispatch error and map old arguments to new ones. + _deprecated_argument( __FUNCTION__, '5.9' ); + if ( 'block_styles' === $types ) { + $types = array( 'styles', 'presets' ); + } elseif ( 'css_variables' === $types ) { + $types = array( 'variables' ); + } else { + $types = array( 'variables', 'styles', 'presets' ); + } + } + + $blocks_metadata = static::get_blocks_metadata(); + $style_nodes = static::get_style_nodes( $this->theme_json, $blocks_metadata ); + $setting_nodes = static::get_setting_nodes( $this->theme_json, $blocks_metadata ); + + $root_style_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $style_nodes, 'selector' ), true ); + $root_settings_key = array_search( static::ROOT_BLOCK_SELECTOR, array_column( $setting_nodes, 'selector' ), true ); + + if ( ! empty( $options['scope'] ) ) { + foreach ( $setting_nodes as &$node ) { + $node['selector'] = static::scope_selector( $options['scope'], $node['selector'] ); + } + foreach ( $style_nodes as &$node ) { + $node['selector'] = static::scope_selector( $options['scope'], $node['selector'] ); + } + } + + if ( ! empty( $options['root_selector'] ) ) { + if ( false !== $root_settings_key ) { + $setting_nodes[ $root_settings_key ]['selector'] = $options['root_selector']; + } + if ( false !== $root_style_key ) { + $setting_nodes[ $root_style_key ]['selector'] = $options['root_selector']; + } + } + + $stylesheet = ''; + + if ( in_array( 'variables', $types, true ) ) { + $stylesheet .= $this->get_css_variables( $setting_nodes, $origins ); + } + + if ( in_array( 'styles', $types, true ) ) { + if ( false !== $root_style_key ) { + $stylesheet .= $this->get_root_layout_rules( $style_nodes[ $root_style_key ]['selector'], $style_nodes[ $root_style_key ] ); + } + $stylesheet .= $this->get_block_classes( $style_nodes ); + } elseif ( in_array( 'base-layout-styles', $types, true ) ) { + $root_selector = static::ROOT_BLOCK_SELECTOR; + $columns_selector = '.wp-block-columns'; + if ( ! empty( $options['scope'] ) ) { + $root_selector = static::scope_selector( $options['scope'], $root_selector ); + $columns_selector = static::scope_selector( $options['scope'], $columns_selector ); + } + if ( ! empty( $options['root_selector'] ) ) { + $root_selector = $options['root_selector']; + } + // Base layout styles are provided as part of `styles`, so only output separately if explicitly requested. + // For backwards compatibility, the Columns block is explicitly included, to support a different default gap value. + $base_styles_nodes = array( + array( + 'path' => array( 'styles' ), + 'selector' => $root_selector, + ), + array( + 'path' => array( 'styles', 'blocks', 'core/columns' ), + 'selector' => $columns_selector, + 'name' => 'core/columns', + ), + ); + + foreach ( $base_styles_nodes as $base_style_node ) { + $stylesheet .= $this->get_layout_styles( $base_style_node ); + } + } + + if ( in_array( 'presets', $types, true ) ) { + $stylesheet .= $this->get_preset_classes( $setting_nodes, $origins ); + } + + // Load the custom CSS last so it has the highest specificity. + if ( in_array( 'custom-css', $types, true ) ) { + $stylesheet .= _wp_array_get( $this->theme_json, array( 'styles', 'css' ) ); + } + + return $stylesheet; + } } diff --git a/lib/compat/wordpress-6.2/class-wp-theme-json-resolver-6-2.php b/lib/compat/wordpress-6.2/class-wp-theme-json-resolver-6-2.php index 2cce8cec7e46e1..110c8bac7b147c 100644 --- a/lib/compat/wordpress-6.2/class-wp-theme-json-resolver-6-2.php +++ b/lib/compat/wordpress-6.2/class-wp-theme-json-resolver-6-2.php @@ -168,4 +168,63 @@ public static function get_merged_data( $origin = 'custom' ) { $result->set_spacing_sizes(); return $result; } + + /** + * Returns the user's origin config. + * + * @since 6.2 Added check for the WP_Theme_JSON_Gutenberg class to prevent $user + * values set in core fron overriding the new custom css values added to VALID_STYLES. + * This does not need to be backported to core as the new VALID_STYLES[css] value will + * be added to core with 6.2. + * + * @return WP_Theme_JSON_Gutenberg Entity that holds styles for user data. + */ + public static function get_user_data() { + if ( null !== static::$user && static::$user instanceof WP_Theme_JSON_Gutenberg ) { + return static::$user; + } + + $config = array(); + $user_cpt = static::get_user_data_from_wp_global_styles( wp_get_theme() ); + + if ( array_key_exists( 'post_content', $user_cpt ) ) { + $decoded_data = json_decode( $user_cpt['post_content'], true ); + + $json_decoding_error = json_last_error(); + if ( JSON_ERROR_NONE !== $json_decoding_error ) { + trigger_error( 'Error when decoding a theme.json schema for user data. ' . json_last_error_msg() ); + /** + * Filters the data provided by the user for global styles & settings. + * + * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data. + */ + $theme_json = apply_filters( 'wp_theme_json_data_user', new WP_Theme_JSON_Data_Gutenberg( $config, 'custom' ) ); + $config = $theme_json->get_data(); + return new WP_Theme_JSON_Gutenberg( $config, 'custom' ); + } + + // Very important to verify if the flag isGlobalStylesUserThemeJSON is true. + // If is not true the content was not escaped and is not safe. + if ( + is_array( $decoded_data ) && + isset( $decoded_data['isGlobalStylesUserThemeJSON'] ) && + $decoded_data['isGlobalStylesUserThemeJSON'] + ) { + unset( $decoded_data['isGlobalStylesUserThemeJSON'] ); + $config = $decoded_data; + } + } + + /** + * Filters the data provided by the user for global styles & settings. + * + * @param WP_Theme_JSON_Data_Gutenberg Class to access and update the underlying data. + */ + $theme_json = apply_filters( 'wp_theme_json_data_user', new WP_Theme_JSON_Data_Gutenberg( $config, 'custom' ) ); + $config = $theme_json->get_data(); + + static::$user = new WP_Theme_JSON_Gutenberg( $config, 'custom' ); + + return static::$user; + } } diff --git a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php index 1bf1ea23b6417f..b2188a92474f63 100644 --- a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php +++ b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php @@ -85,7 +85,7 @@ function gutenberg_get_global_stylesheet( $types = array() ) { if ( empty( $types ) && ! $supports_theme_json ) { $types = array( 'variables', 'presets', 'base-layout-styles' ); } elseif ( empty( $types ) ) { - $types = array( 'variables', 'styles', 'presets' ); + $types = array( 'variables', 'styles', 'presets', 'custom-css' ); } /* diff --git a/lib/compat/wordpress-6.2/rest-api.php b/lib/compat/wordpress-6.2/rest-api.php index 4541ce0ae158c6..4900a622b6c01b 100644 --- a/lib/compat/wordpress-6.2/rest-api.php +++ b/lib/compat/wordpress-6.2/rest-api.php @@ -92,3 +92,12 @@ function gutenberg_pattern_directory_collection_params_6_2( $query_params ) { return $query_params; } add_filter( 'rest_pattern_directory_collection_params', 'gutenberg_pattern_directory_collection_params_6_2' ); + +/** + * Registers the Global Styles REST API routes. + */ +function gutenberg_register_global_styles_endpoints() { + $editor_settings = new Gutenberg_REST_Global_Styles_Controller_6_2(); + $editor_settings->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' ); diff --git a/lib/load.php b/lib/load.php index 3d871e607d7fa5..b634b85b977e38 100644 --- a/lib/load.php +++ b/lib/load.php @@ -46,6 +46,7 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-pattern-directory-controller-6-2.php'; require_once __DIR__ . '/compat/wordpress-6.2/rest-api.php'; require_once __DIR__ . '/compat/wordpress-6.2/block-patterns.php'; + require_once __DIR__ . '/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php'; // Experimental. if ( ! class_exists( 'WP_Rest_Customizer_Nonces' ) ) { @@ -60,8 +61,8 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/plugin/edit-site-routes-backwards-compat.php'; // WordPress 6.1 compat. -require __DIR__ . '/compat/wordpress-6.1/blocks.php'; require __DIR__ . '/compat/wordpress-6.1/block-editor-settings.php'; +require __DIR__ . '/compat/wordpress-6.1/blocks.php'; require __DIR__ . '/compat/wordpress-6.1/persisted-preferences.php'; require __DIR__ . '/compat/wordpress-6.1/get-global-styles-and-settings.php'; require __DIR__ . '/compat/wordpress-6.1/class-wp-theme-json-data-gutenberg.php'; @@ -83,6 +84,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-6.2/class-wp-theme-json-6-2.php'; require __DIR__ . '/compat/wordpress-6.2/edit-form-blocks.php'; require __DIR__ . '/compat/wordpress-6.2/site-editor.php'; +require __DIR__ . '/compat/wordpress-6.2/block-editor-settings.php'; // Experimental features. remove_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); // Turns off WP 6.0's stopgap handler for Webfonts API. diff --git a/packages/edit-site/src/components/global-styles/custom-css.js b/packages/edit-site/src/components/global-styles/custom-css.js new file mode 100644 index 00000000000000..6c7d3de1cf06e5 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/custom-css.js @@ -0,0 +1,73 @@ +/** + * WordPress dependencies + */ +import { TextareaControl, Panel, PanelBody } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { useStyle } from './hooks'; + +function CustomCSSControl() { + const [ customCSS, setCustomCSS ] = useStyle( 'css' ); + const [ themeCSS ] = useStyle( 'css', null, 'base' ); + const ignoreThemeCustomCSS = '/* IgnoreThemeCustomCSS */'; + + // If there is custom css from theme.json show it in the edit box + // so the user can selectively overwrite it, rather than have the user CSS + // completely overwrite the theme CSS by default. + const themeCustomCSS = + ! customCSS && themeCSS + ? `/* ${ __( + 'Theme Custom CSS start' + ) } */\n${ themeCSS }\n/* ${ __( 'Theme Custom CSS end' ) } */` + : undefined; + + function handleOnChange( value ) { + // If there is theme custom CSS, but the user clears the input box then save the + // ignoreThemeCustomCSS string so that the theme custom CSS is not re-applied. + if ( themeCSS && value === '' ) { + setCustomCSS( ignoreThemeCustomCSS ); + return; + } + setCustomCSS( value ); + } + + const originalThemeCustomCSS = + themeCSS && customCSS && themeCustomCSS !== customCSS + ? themeCSS + : undefined; + + return ( + <> + handleOnChange( value ) } + rows={ 15 } + className="edit-site-global-styles__custom-css-input" + spellCheck={ false } + help={ __( + "Enter your custom CSS in the textarea and preview in the editor. Changes won't take effect until you've saved the template." + ) } + /> + { originalThemeCustomCSS && ( + + +
+							{ originalThemeCustomCSS }
+						
+
+
+ ) } + + ); +} + +export default CustomCSSControl; diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index 86e8aa2d2a6413..c767f1a488cbfd 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -138,7 +138,11 @@ export function useStyle( path, blockName, source = 'all' ) { result = getValueFromVariable( mergedConfig, blockName, - get( userConfig, finalPath ) ?? get( baseConfig, finalPath ) + // The stlyes.css path is allowed to be empty, so don't revert to base if undefined. + finalPath === 'styles.css' + ? get( userConfig, finalPath ) + : get( userConfig, finalPath ) ?? + get( baseConfig, finalPath ) ); break; case 'user': diff --git a/packages/edit-site/src/components/global-styles/screen-css.js b/packages/edit-site/src/components/global-styles/screen-css.js new file mode 100644 index 00000000000000..74480a0677bd16 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/screen-css.js @@ -0,0 +1,33 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { __experimentalVStack as VStack } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import ScreenHeader from './header'; +import Subtitle from './subtitle'; +import CustomCSSControl from './custom-css'; + +function ScreenCSS() { + return ( + <> + +
+ + { __( 'ADDITIONAL CSS' ) } + + +
+ + ); +} + +export default ScreenCSS; diff --git a/packages/edit-site/src/components/global-styles/screen-root.js b/packages/edit-site/src/components/global-styles/screen-root.js index 0674b56319c70b..ecb68d27b7f4d7 100644 --- a/packages/edit-site/src/components/global-styles/screen-root.js +++ b/packages/edit-site/src/components/global-styles/screen-root.js @@ -75,7 +75,8 @@ function ScreenRoot() { paddingTop={ 2 } /* * 13px matches the text inset of the NavigationButton (12px padding, plus the width of the button's border). - * This is an ad hoc override for this particular instance only and should be reconsidered before making into a pattern. + * This is an ad hoc override for this instance and the Addtional CSS option below. Other options for matching the + * the nav button inset should be looked at before reusing further. */ paddingX="13px" marginBottom={ 4 } @@ -98,6 +99,34 @@ function ScreenRoot() { + + + + + + { __( + 'Add your own CSS to customize the appearance and layout of your site.' + ) } + + + + + { __( 'Custom' ) } + + + + + ); } diff --git a/packages/edit-site/src/components/global-styles/style.scss b/packages/edit-site/src/components/global-styles/style.scss index 16c9b9c418dcad..5616a068b594c8 100644 --- a/packages/edit-site/src/components/global-styles/style.scss +++ b/packages/edit-site/src/components/global-styles/style.scss @@ -31,7 +31,8 @@ $block-preview-height: 150px; } .edit-site-global-styles-screen-heading-color, -.edit-site-global-styles-screen-typography { +.edit-site-global-styles-screen-typography, +.edit-site-global-styles-screen-css { margin: $grid-unit-20; } @@ -133,3 +134,15 @@ $block-preview-height: 150px; border: $gray-200 $border-width solid; border-radius: $radius-block-ui; } + +.edit-site-global-styles__custom-css-input textarea { + font-family: $editor_html_font; +} + +.edit-site-global-styles__custom-css-theme-css { + width: 100%; + line-break: anywhere; + white-space: break-spaces; + max-height: 200px; + overflow-y: scroll; +} diff --git a/packages/edit-site/src/components/global-styles/ui.js b/packages/edit-site/src/components/global-styles/ui.js index 532a211597a81f..d7c9eed7d8474b 100644 --- a/packages/edit-site/src/components/global-styles/ui.js +++ b/packages/edit-site/src/components/global-styles/ui.js @@ -27,6 +27,7 @@ import ScreenLayout from './screen-layout'; import ScreenStyleVariations from './screen-style-variations'; import ScreenBorder from './screen-border'; import StyleBook from '../style-book'; +import ScreenCSS from './screen-css'; function GlobalStylesNavigationScreen( { className, ...props } ) { return ( @@ -191,6 +192,9 @@ function GlobalStylesUI( { isStyleBookOpened, onCloseStyleBook } ) { { isStyleBookOpened && ( ) } + + + ); } diff --git a/packages/edit-site/src/components/global-styles/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/use-global-styles-output.js index b32201fe2d2c22..d83693757b035c 100644 --- a/packages/edit-site/src/components/global-styles/use-global-styles-output.js +++ b/packages/edit-site/src/components/global-styles/use-global-styles-output.js @@ -919,6 +919,11 @@ export function useGlobalStylesOutput() { css: globalStyles, isGlobalStyles: true, }, + // Load custom CSS in own stylesheet so that any invalid CSS entered in the input won't break all the global styles in the editor. + { + css: mergedConfig.styles.css ?? '', + isGlobalStyles: true, + }, ]; return [ stylesheets, mergedConfig.settings, filters ]; diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 74306fa5d8ae32..a5413766ca7ddd 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -1497,4 +1497,18 @@ public function test_update_separator_declarations() { $this->assertEquals( $expected, $stylesheet ); } + + public function test_get_stylesheet_handles_custom_css() { + $theme_json = new WP_Theme_JSON_Gutenberg( + array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'css' => 'body { color:purple; }', + ), + ) + ); + + $custom_css = 'body { color:purple; }'; + $this->assertEquals( $custom_css, $theme_json->get_stylesheet( array( 'custom-css' ) ) ); + } } From 0dd1da54fa122fb06192ba29333b2ddfcb39e5e9 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Tue, 13 Dec 2022 13:11:41 +0800 Subject: [PATCH 13/73] Query: Remove color block supports (#46147) --- docs/reference-guides/core-blocks.md | 4 +- .../src/post-template/block.json | 8 + packages/block-library/src/query/block.json | 8 - .../block-library/src/query/deprecated.js | 345 +++++++++++++----- .../blocks/core__query__deprecated-3.html | 23 ++ .../blocks/core__query__deprecated-3.json | 133 +++++++ .../core__query__deprecated-3.parsed.json | 128 +++++++ .../core__query__deprecated-3.serialized.html | 25 ++ 8 files changed, 577 insertions(+), 97 deletions(-) create mode 100644 test/integration/fixtures/blocks/core__query__deprecated-3.html create mode 100644 test/integration/fixtures/blocks/core__query__deprecated-3.json create mode 100644 test/integration/fixtures/blocks/core__query__deprecated-3.parsed.json create mode 100644 test/integration/fixtures/blocks/core__query__deprecated-3.serialized.html diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index e3601a1f0a5093..d2d66fc0d12de3 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -572,7 +572,7 @@ Contains the block elements used to render a post, like the title, date, feature - **Name:** core/post-template - **Category:** theme -- **Supports:** align, typography (fontSize, lineHeight), ~~html~~, ~~reusable~~ +- **Supports:** align, color (background, gradients, link, text), typography (fontSize, lineHeight), ~~html~~, ~~reusable~~ - **Attributes:** ## Post Terms @@ -617,7 +617,7 @@ An advanced block that allows displaying post types based on different query par - **Name:** core/query - **Category:** theme -- **Supports:** align (full, wide), color (background, gradients, link, text), ~~html~~ +- **Supports:** align (full, wide), ~~html~~ - **Attributes:** displayLayout, namespace, query, queryId, tagName ## No results diff --git a/packages/block-library/src/post-template/block.json b/packages/block-library/src/post-template/block.json index 380b6d55f71fad..bc9910b47d1dc5 100644 --- a/packages/block-library/src/post-template/block.json +++ b/packages/block-library/src/post-template/block.json @@ -22,6 +22,14 @@ "__experimentalLayout": { "allowEditing": false }, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, "typography": { "fontSize": true, "lineHeight": true, diff --git a/packages/block-library/src/query/block.json b/packages/block-library/src/query/block.json index cd09e22ee57f75..1974761962ec91 100644 --- a/packages/block-library/src/query/block.json +++ b/packages/block-library/src/query/block.json @@ -50,14 +50,6 @@ "supports": { "align": [ "wide", "full" ], "html": false, - "color": { - "gradients": true, - "link": true, - "__experimentalDefaultControls": { - "background": true, - "text": true - } - }, "__experimentalLayout": true }, "editorStyle": "wp-block-query-editor" diff --git a/packages/block-library/src/query/deprecated.js b/packages/block-library/src/query/deprecated.js index 42ac3dc4f1c49d..d38ab3328559be 100644 --- a/packages/block-library/src/query/deprecated.js +++ b/packages/block-library/src/query/deprecated.js @@ -1,12 +1,18 @@ /** * WordPress dependencies */ +import { createBlock } from '@wordpress/blocks'; import { InnerBlocks, useInnerBlocksProps, useBlockProps, } from '@wordpress/block-editor'; +/** + * Internal dependencies + */ +import cleanEmptyObject from '../utils/clean-empty-object'; + const migrateToTaxQuery = ( attributes ) => { const { query } = attributes; const { categoryIds, tagIds, ...newQuery } = query; @@ -25,106 +31,271 @@ const migrateToTaxQuery = ( attributes ) => { }; }; -const deprecated = [ - // Version with `categoryIds and tagIds`. - { - attributes: { - queryId: { - type: 'number', - }, - query: { - type: 'object', - default: { - perPage: null, - pages: 0, - offset: 0, - postType: 'post', - categoryIds: [], - tagIds: [], - order: 'desc', - orderBy: 'date', - author: '', - search: '', - exclude: [], - sticky: '', - inherit: true, - }, +const hasSingleInnerGroupBlock = ( innerBlocks = [] ) => + innerBlocks.length === 1 && innerBlocks[ 0 ].name === 'core/group'; + +// Version with NO wrapper `div` element. +const v1 = { + attributes: { + queryId: { + type: 'number', + }, + query: { + type: 'object', + default: { + perPage: null, + pages: 0, + offset: 0, + postType: 'post', + categoryIds: [], + tagIds: [], + order: 'desc', + orderBy: 'date', + author: '', + search: '', + exclude: [], + sticky: '', + inherit: true, }, - tagName: { - type: 'string', - default: 'div', + }, + layout: { + type: 'object', + default: { + type: 'list', }, - displayLayout: { - type: 'object', - default: { - type: 'list', - }, + }, + }, + supports: { + html: false, + }, + migrate( attributes ) { + const withTaxQuery = migrateToTaxQuery( attributes ); + const { layout, ...restWithTaxQuery } = withTaxQuery; + return { + ...restWithTaxQuery, + displayLayout: withTaxQuery.layout, + }; + }, + save() { + return ; + }, +}; + +// Version with `categoryIds and tagIds`. +const v2 = { + attributes: { + queryId: { + type: 'number', + }, + query: { + type: 'object', + default: { + perPage: null, + pages: 0, + offset: 0, + postType: 'post', + categoryIds: [], + tagIds: [], + order: 'desc', + orderBy: 'date', + author: '', + search: '', + exclude: [], + sticky: '', + inherit: true, }, }, - supports: { - align: [ 'wide', 'full' ], - html: false, - color: { - gradients: true, - link: true, + tagName: { + type: 'string', + default: 'div', + }, + displayLayout: { + type: 'object', + default: { + type: 'list', }, - __experimentalLayout: true, }, - isEligible: ( { query: { categoryIds, tagIds } = {} } ) => - categoryIds || tagIds, - migrate: migrateToTaxQuery, - save( { attributes: { tagName: Tag = 'div' } } ) { - const blockProps = useBlockProps.save(); - const innerBlocksProps = useInnerBlocksProps.save( blockProps ); - return ; + }, + supports: { + align: [ 'wide', 'full' ], + html: false, + color: { + gradients: true, + link: true, }, + __experimentalLayout: true, }, - // Version with NO wrapper `div` element. - { - attributes: { - queryId: { - type: 'number', - }, - query: { - type: 'object', - default: { - perPage: null, - pages: 0, - offset: 0, - postType: 'post', - categoryIds: [], - tagIds: [], - order: 'desc', - orderBy: 'date', - author: '', - search: '', - exclude: [], - sticky: '', - inherit: true, - }, - }, - layout: { - type: 'object', - default: { - type: 'list', - }, + isEligible: ( { query: { categoryIds, tagIds } = {} } ) => + categoryIds || tagIds, + migrate: migrateToTaxQuery, + save( { attributes: { tagName: Tag = 'div' } } ) { + const blockProps = useBlockProps.save(); + const innerBlocksProps = useInnerBlocksProps.save( blockProps ); + return ; + }, +}; + +// Version with color support prior to moving it to the PostTemplate block. +const v3 = { + attributes: { + queryId: { + type: 'number', + }, + query: { + type: 'object', + default: { + perPage: null, + pages: 0, + offset: 0, + postType: 'post', + order: 'desc', + orderBy: 'date', + author: '', + search: '', + exclude: [], + sticky: '', + inherit: true, + taxQuery: null, + parents: [], }, }, - supports: { - html: false, + tagName: { + type: 'string', + default: 'div', }, - migrate( attributes ) { - const withTaxQuery = migrateToTaxQuery( attributes ); - const { layout, ...restWithTaxQuery } = withTaxQuery; - return { - ...restWithTaxQuery, - displayLayout: withTaxQuery.layout, - }; + displayLayout: { + type: 'object', + default: { + type: 'list', + }, }, - save() { - return ; + namespace: { + type: 'string', + }, + }, + supports: { + align: [ 'wide', 'full' ], + html: false, + color: { + gradients: true, + link: true, + __experimentalDefaultControls: { + background: true, + text: true, + }, }, + __experimentalLayout: true, }, -]; + isEligible( attributes ) { + const { style, backgroundColor, gradient, textColor } = attributes; + return ( + backgroundColor || + gradient || + textColor || + style?.color || + style?.elements?.link + ); + }, + migrate: ( attributes, innerBlocks ) => { + // Remove color style attributes from the Query block. + const { + style, + backgroundColor, + gradient, + textColor, + ...newAttributes + } = attributes; + + const hasColorStyles = + backgroundColor || + gradient || + textColor || + style?.color || + style?.elements?.link; + + // If the query block doesn't currently have any color styles, + // nothing needs migrating. + if ( ! hasColorStyles ) { + return [ attributes, innerBlocks ]; + } + + // Clean color values from style attribute object. + if ( style ) { + newAttributes.style = cleanEmptyObject( { + ...style, + color: undefined, + elements: { + ...style.elements, + link: undefined, + }, + } ); + } + + // If the inner blocks are already wrapped in a single group + // block, add the color support styles to that group block. + if ( hasSingleInnerGroupBlock( innerBlocks ) ) { + const groupBlock = innerBlocks[ 0 ]; + + // Create new styles for the group block. + const hasStyles = + style?.color || + style?.elements?.link || + groupBlock.attributes.style; + + const newStyles = hasStyles + ? cleanEmptyObject( { + ...groupBlock.attributes.style, + color: style?.color, + elements: style?.elements?.link + ? { link: style?.elements?.link } + : undefined, + } ) + : undefined; + + // Create a new Group block from the original. + const updatedGroupBlock = createBlock( + 'core/group', + { + ...groupBlock.attributes, + backgroundColor, + gradient, + textColor, + style: newStyles, + }, + groupBlock.innerBlocks + ); + + return [ newAttributes, [ updatedGroupBlock ] ]; + } + + // When we don't have a single wrapping group block for the inner + // blocks, wrap the current inner blocks in a group applying the + // color styles to that. + const newGroupBlock = createBlock( + 'core/group', + { + backgroundColor, + gradient, + textColor, + style: cleanEmptyObject( { + color: style?.color, + elements: style?.elements?.link + ? { link: style?.elements?.link } + : undefined, + } ), + }, + innerBlocks + ); + + return [ newAttributes, [ newGroupBlock ] ]; + }, + save( { attributes: { tagName: Tag = 'div' } } ) { + const blockProps = useBlockProps.save(); + const innerBlocksProps = useInnerBlocksProps.save( blockProps ); + return ; + }, +}; + +const deprecated = [ v3, v2, v1 ]; export default deprecated; diff --git a/test/integration/fixtures/blocks/core__query__deprecated-3.html b/test/integration/fixtures/blocks/core__query__deprecated-3.html new file mode 100644 index 00000000000000..9da20c78184e2a --- /dev/null +++ b/test/integration/fixtures/blocks/core__query__deprecated-3.html @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/test/integration/fixtures/blocks/core__query__deprecated-3.json b/test/integration/fixtures/blocks/core__query__deprecated-3.json new file mode 100644 index 00000000000000..bb1478cd676f22 --- /dev/null +++ b/test/integration/fixtures/blocks/core__query__deprecated-3.json @@ -0,0 +1,133 @@ +[ + { + "name": "core/query", + "isValid": true, + "attributes": { + "queryId": 3, + "query": { + "perPage": 3, + "pages": 0, + "offset": 0, + "postType": "post", + "order": "desc", + "orderBy": "date", + "author": "", + "search": "", + "exclude": [], + "sticky": "", + "inherit": false + }, + "tagName": "div", + "displayLayout": { + "type": "flex", + "columns": 3 + }, + "align": "wide" + }, + "innerBlocks": [ + { + "name": "core/group", + "isValid": true, + "attributes": { + "tagName": "div", + "textColor": "pale-cyan-blue", + "style": { + "color": { + "background": "#284d5f" + }, + "elements": { + "link": { + "color": { + "text": "var:preset|color|luminous-vivid-amber" + } + } + } + } + }, + "innerBlocks": [ + { + "name": "core/post-template", + "isValid": true, + "attributes": { + "fontSize": "large" + }, + "innerBlocks": [ + { + "name": "core/post-title", + "isValid": true, + "attributes": { + "level": 2, + "isLink": false, + "rel": "", + "linkTarget": "_self" + }, + "innerBlocks": [] + }, + { + "name": "core/post-date", + "isValid": true, + "attributes": { + "isLink": false, + "displayType": "date" + }, + "innerBlocks": [] + }, + { + "name": "core/post-excerpt", + "isValid": true, + "attributes": { + "showMoreOnNewLine": true + }, + "innerBlocks": [] + } + ] + }, + { + "name": "core/query-pagination", + "isValid": true, + "attributes": { + "paginationArrow": "none" + }, + "innerBlocks": [ + { + "name": "core/query-pagination-previous", + "isValid": true, + "attributes": {}, + "innerBlocks": [] + }, + { + "name": "core/query-pagination-numbers", + "isValid": true, + "attributes": {}, + "innerBlocks": [] + }, + { + "name": "core/query-pagination-next", + "isValid": true, + "attributes": {}, + "innerBlocks": [] + } + ] + }, + { + "name": "core/query-no-results", + "isValid": true, + "attributes": {}, + "innerBlocks": [ + { + "name": "core/paragraph", + "isValid": true, + "attributes": { + "content": "No results found.", + "dropCap": false, + "placeholder": "Add text or blocks that will display when a query returns no results." + }, + "innerBlocks": [] + } + ] + } + ] + } + ] + } +] diff --git a/test/integration/fixtures/blocks/core__query__deprecated-3.parsed.json b/test/integration/fixtures/blocks/core__query__deprecated-3.parsed.json new file mode 100644 index 00000000000000..a26e573bac314b --- /dev/null +++ b/test/integration/fixtures/blocks/core__query__deprecated-3.parsed.json @@ -0,0 +1,128 @@ +[ + { + "blockName": "core/query", + "attrs": { + "queryId": 3, + "query": { + "perPage": 3, + "pages": 0, + "offset": 0, + "postType": "post", + "order": "desc", + "orderBy": "date", + "author": "", + "search": "", + "exclude": [], + "sticky": "", + "inherit": false + }, + "displayLayout": { + "type": "flex", + "columns": 3 + }, + "align": "wide", + "style": { + "color": { + "background": "#284d5f" + }, + "elements": { + "link": { + "color": { + "text": "var:preset|color|luminous-vivid-amber" + } + } + } + }, + "textColor": "pale-cyan-blue" + }, + "innerBlocks": [ + { + "blockName": "core/post-template", + "attrs": { + "fontSize": "large" + }, + "innerBlocks": [ + { + "blockName": "core/post-title", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + }, + { + "blockName": "core/post-date", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + }, + { + "blockName": "core/post-excerpt", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + } + ], + "innerHTML": "\n\n\n\n\n\n", + "innerContent": [ "\n", null, "\n\n", null, "\n\n", null, "\n" ] + }, + { + "blockName": "core/query-pagination", + "attrs": {}, + "innerBlocks": [ + { + "blockName": "core/query-pagination-previous", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + }, + { + "blockName": "core/query-pagination-numbers", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + }, + { + "blockName": "core/query-pagination-next", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + } + ], + "innerHTML": "\n\n\n\n\n\n", + "innerContent": [ "\n", null, "\n\n", null, "\n\n", null, "\n" ] + }, + { + "blockName": "core/query-no-results", + "attrs": {}, + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": { + "placeholder": "Add text or blocks that will display when a query returns no results." + }, + "innerBlocks": [], + "innerHTML": "\n

No results found.

\n", + "innerContent": [ "\n

No results found.

\n" ] + } + ], + "innerHTML": "\n\n", + "innerContent": [ "\n", null, "\n" ] + } + ], + "innerHTML": "\n
\n\n\n\n
\n", + "innerContent": [ + "\n
", + null, + "\n\n", + null, + "\n\n", + null, + "
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__query__deprecated-3.serialized.html b/test/integration/fixtures/blocks/core__query__deprecated-3.serialized.html new file mode 100644 index 00000000000000..edbf5b1a0557b3 --- /dev/null +++ b/test/integration/fixtures/blocks/core__query__deprecated-3.serialized.html @@ -0,0 +1,25 @@ + +
+ +
+ From fcc158b966555c92552ab5cabb015d03ea74da08 Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Tue, 13 Dec 2022 08:46:15 +0100 Subject: [PATCH 14/73] Mobile - Inserter - Use .then instead of .done since the polyfill is now deprecated. (#46460) --- packages/block-editor/src/components/inserter/index.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js index 7dcc0fdbef0e13..a3e6981e6ecfc7 100644 --- a/packages/block-editor/src/components/inserter/index.native.js +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -216,7 +216,7 @@ export class Inserter extends Component { } onInserterToggledAnnouncement( isOpen ) { - AccessibilityInfo.isScreenReaderEnabled().done( ( isEnabled ) => { + AccessibilityInfo.isScreenReaderEnabled().then( ( isEnabled ) => { if ( isEnabled ) { const isIOS = Platform.OS === 'ios'; const announcement = isOpen From 98409e95b2d90579c6545bc60e7268da21a14df4 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Tue, 13 Dec 2022 10:32:48 +0200 Subject: [PATCH 15/73] "custom-css" is now an acceptable value. (#46493) --- lib/compat/wordpress-6.2/get-global-styles-and-settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php index b2188a92474f63..8556a0be1663f4 100644 --- a/lib/compat/wordpress-6.2/get-global-styles-and-settings.php +++ b/lib/compat/wordpress-6.2/get-global-styles-and-settings.php @@ -63,7 +63,7 @@ function wp_theme_has_theme_json_clean_cache() { * Returns the stylesheet resulting of merging core, theme, and user data. * * @param array $types Types of styles to load. Optional. - * It accepts 'variables', 'styles', 'presets' as values. + * It accepts 'variables', 'styles', 'presets', 'custom-css' as values. * If empty, it'll load all for themes with theme.json support * and only [ 'variables', 'presets' ] for themes without theme.json support. * From 570e7d99c5e4e80f654be2f65aecb45d978469dd Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 13 Dec 2022 10:55:26 +0100 Subject: [PATCH 16/73] Fix content locked patterns (#46494) --- packages/block-editor/src/store/selectors.js | 4 +-- ...ocks-with-content-only-lock-1-chromium.txt | 5 +++ .../editor/various/content-only-lock.spec.js | 31 +++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 test/e2e/specs/editor/various/__snapshots__/Content-only-lock-should-be-able-to-edit-the-content-of-blocks-with-content-only-lock-1-chromium.txt create mode 100644 test/e2e/specs/editor/various/content-only-lock.spec.js diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 36d3f42ae58bc7..694f54bacdee01 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2690,8 +2690,8 @@ export const __unstableGetContentLockingParent = createSelector( ( state, clientId ) => { let current = clientId; let result; - while ( !! state.blocks.parents[ current ] ) { - current = state.blocks.parents[ current ]; + while ( state.blocks.parents.has( current ) ) { + current = state.blocks.parents.get( current ); if ( getTemplateLock( state, current ) === 'contentOnly' ) { result = current; } diff --git a/test/e2e/specs/editor/various/__snapshots__/Content-only-lock-should-be-able-to-edit-the-content-of-blocks-with-content-only-lock-1-chromium.txt b/test/e2e/specs/editor/various/__snapshots__/Content-only-lock-should-be-able-to-edit-the-content-of-blocks-with-content-only-lock-1-chromium.txt new file mode 100644 index 00000000000000..18f0839c8dc89f --- /dev/null +++ b/test/e2e/specs/editor/various/__snapshots__/Content-only-lock-should-be-able-to-edit-the-content-of-blocks-with-content-only-lock-1-chromium.txt @@ -0,0 +1,5 @@ + +
+

Hello World

+
+ \ No newline at end of file diff --git a/test/e2e/specs/editor/various/content-only-lock.spec.js b/test/e2e/specs/editor/various/content-only-lock.spec.js new file mode 100644 index 00000000000000..41626ed986b8f2 --- /dev/null +++ b/test/e2e/specs/editor/various/content-only-lock.spec.js @@ -0,0 +1,31 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Content-only lock', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'should be able to edit the content of blocks with content-only lock', async ( { + editor, + page, + pageUtils, + } ) => { + // Add content only locked block in the code editor + await pageUtils.pressKeyWithModifier( 'secondary', 'M' ); // Emulates CTRL+Shift+Alt + M => toggle code editor + await page.click( '.editor-post-text-editor' ); + await page.keyboard + .type( ` +
+

Hello

+
+ ` ); + await pageUtils.pressKeyWithModifier( 'secondary', 'M' ); + + await page.click( 'role=document[name="Paragraph block"i]' ); + await page.keyboard.type( ' World' ); + expect( await editor.getEditedPostContent() ).toMatchSnapshot(); + } ); +} ); From 0819ea01b7afe476df524b017b1919cc962802ea Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Tue, 13 Dec 2022 12:16:54 +0200 Subject: [PATCH 17/73] Lodash: Refactor editor away from _.find() (#46464) --- packages/editor/src/components/page-attributes/parent.js | 5 ++--- packages/editor/src/components/post-format/index.js | 8 +------- .../post-publish-panel/maybe-post-format-panel.js | 4 ++-- .../src/components/post-taxonomies/flat-term-selector.js | 6 +++--- .../post-taxonomies/hierarchical-term-selector.js | 4 ++-- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index 4ee2491e17209b..2e402f59415c78 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { get, unescape as unescapeString, find } from 'lodash'; +import { get, unescape as unescapeString } from 'lodash'; import removeAccents from 'remove-accents'; /** @@ -122,8 +122,7 @@ export function PageAttributesParent() { const opts = getOptionsFromTree( tree ); // Ensure the current parent is in the options list. - const optsHasParent = find( - opts, + const optsHasParent = opts.find( ( item ) => item.value === parentPostId ); if ( parentPost && ! optsHasParent ) { diff --git a/packages/editor/src/components/post-format/index.js b/packages/editor/src/components/post-format/index.js index 4f3ec2c6966937..518ec2c0d26bca 100644 --- a/packages/editor/src/components/post-format/index.js +++ b/packages/editor/src/components/post-format/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { find } from 'lodash'; - /** * WordPress dependencies */ @@ -69,8 +64,7 @@ export default function PostFormat() { supportedFormats?.includes( format.id ) || postFormat === format.id ); } ); - const suggestion = find( - formats, + const suggestion = formats.find( ( format ) => format.id === suggestedFormat ); diff --git a/packages/editor/src/components/post-publish-panel/maybe-post-format-panel.js b/packages/editor/src/components/post-publish-panel/maybe-post-format-panel.js index e2aff854a7a9eb..106665c9ea69e6 100644 --- a/packages/editor/src/components/post-publish-panel/maybe-post-format-panel.js +++ b/packages/editor/src/components/post-publish-panel/maybe-post-format-panel.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { find, get } from 'lodash'; +import { get } from 'lodash'; /** * WordPress dependencies @@ -21,7 +21,7 @@ const getSuggestion = ( supportedFormats, suggestedPostFormat ) => { const formats = POST_FORMATS.filter( ( format ) => supportedFormats?.includes( format.id ) ); - return find( formats, ( format ) => format.id === suggestedPostFormat ); + return formats.find( ( format ) => format.id === suggestedPostFormat ); }; const PostFormatSuggestion = ( { diff --git a/packages/editor/src/components/post-taxonomies/flat-term-selector.js b/packages/editor/src/components/post-taxonomies/flat-term-selector.js index 2195628cdd1681..b47e7bd3c9dda3 100644 --- a/packages/editor/src/components/post-taxonomies/flat-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/flat-term-selector.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { find, get } from 'lodash'; +import { get } from 'lodash'; import escapeHtml from 'escape-html'; /** @@ -50,7 +50,7 @@ const isSameTermName = ( termA, termB ) => const termNamesToIds = ( names, terms ) => { return names.map( ( termName ) => - find( terms, ( term ) => isSameTermName( term.name, termName ) ).id + terms.find( ( term ) => isSameTermName( term.name, termName ) ).id ); }; @@ -203,7 +203,7 @@ export function FlatTermSelector( { slug } ) { const newTermNames = uniqueTerms.filter( ( termName ) => - ! find( availableTerms, ( term ) => + ! availableTerms.find( ( term ) => isSameTermName( term.name, termName ) ) ); diff --git a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js index 7663113dc03207..58734bea267895 100644 --- a/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js +++ b/packages/editor/src/components/post-taxonomies/hierarchical-term-selector.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { find, get, unescape as unescapeString } from 'lodash'; +import { get, unescape as unescapeString } from 'lodash'; /** * WordPress dependencies @@ -95,7 +95,7 @@ export function sortBySelected( termsTree, terms ) { * @return {Object} Term object. */ export function findTerm( terms, parent, name ) { - return find( terms, ( term ) => { + return terms.find( ( term ) => { return ( ( ( ! term.parent && ! parent ) || parseInt( term.parent ) === parseInt( parent ) ) && From f5582817aa2c45ddc7d52dac50025fcfc2a438e3 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Tue, 13 Dec 2022 12:17:10 +0200 Subject: [PATCH 18/73] Lodash: Refactor core-data away from _.find() (#46468) --- packages/core-data/src/actions.js | 13 +++++++++---- packages/core-data/src/entities.js | 10 +++++++--- packages/core-data/src/selectors.ts | 11 ++++++++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index 550264d6232ae3..307e5cef0e2296 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -2,7 +2,6 @@ * External dependencies */ import fastDeepEqual from 'fast-deep-equal/es6'; -import { find } from 'lodash'; import { v4 as uuid } from 'uuid'; /** @@ -256,7 +255,9 @@ export const deleteEntityRecord = ) => async ( { dispatch } ) => { const configs = await dispatch( getOrLoadEntitiesConfig( kind ) ); - const entityConfig = find( configs, { kind, name } ); + const entityConfig = configs.find( + ( config ) => config.kind === kind && config.name === name + ); let error; let deletedRecord = false; if ( ! entityConfig || entityConfig?.__experimentalNoFetch ) { @@ -451,7 +452,9 @@ export const saveEntityRecord = ) => async ( { select, resolveSelect, dispatch } ) => { const configs = await dispatch( getOrLoadEntitiesConfig( kind ) ); - const entityConfig = find( configs, { kind, name } ); + const entityConfig = configs.find( + ( config ) => config.kind === kind && config.name === name + ); if ( ! entityConfig || entityConfig?.__experimentalNoFetch ) { return; } @@ -723,7 +726,9 @@ export const saveEditedEntityRecord = return; } const configs = await dispatch( getOrLoadEntitiesConfig( kind ) ); - const entityConfig = find( configs, { kind, name } ); + const entityConfig = configs.find( + ( config ) => config.kind === kind && config.name === name + ); if ( ! entityConfig ) { return; } diff --git a/packages/core-data/src/entities.js b/packages/core-data/src/entities.js index 0b64c3362e8955..2fc6f4d41d7684 100644 --- a/packages/core-data/src/entities.js +++ b/packages/core-data/src/entities.js @@ -2,7 +2,7 @@ * External dependencies */ import { capitalCase, pascalCase } from 'change-case'; -import { map, find, get } from 'lodash'; +import { map, get } from 'lodash'; /** * WordPress dependencies @@ -288,7 +288,9 @@ export const getMethodName = ( prefix = 'get', usePlural = false ) => { - const entityConfig = find( rootEntitiesConfig, { kind, name } ); + const entityConfig = rootEntitiesConfig.find( + ( config ) => config.kind === kind && config.name === name + ); const kindPrefix = kind === 'root' ? '' : pascalCase( kind ); const nameSuffix = pascalCase( name ) + ( usePlural ? 's' : '' ); const suffix = @@ -313,7 +315,9 @@ export const getOrLoadEntitiesConfig = return configs; } - const loader = find( additionalEntityConfigLoaders, { kind } ); + const loader = additionalEntityConfigLoaders.find( + ( l ) => l.kind === kind + ); if ( ! loader ) { return []; } diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts index 93306d2226e7a2..9998d67728e745 100644 --- a/packages/core-data/src/selectors.ts +++ b/packages/core-data/src/selectors.ts @@ -2,7 +2,7 @@ * External dependencies */ import createSelector from 'rememo'; -import { set, map, find, get } from 'lodash'; +import { set, map, get } from 'lodash'; /** * WordPress dependencies @@ -218,7 +218,9 @@ export function getEntityConfig( kind: string, name: string ): any { - return find( state.entities.config, { kind, name } ); + return state.entities.config?.find( + ( config ) => config.kind === kind && config.name === name + ); } /** @@ -1129,7 +1131,10 @@ export function getAutosave< EntityRecord extends ET.EntityRecord< any > >( } const autosaves = state.autosaves[ postId ]; - return find( autosaves, { author: authorId } ) as EntityRecord | undefined; + + return autosaves?.find( + ( autosave: any ) => autosave.author === authorId + ) as EntityRecord | undefined; } /** From dd0504bd34c29b5b2824d82c8d2bb3a8d0f071ec Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Tue, 13 Dec 2022 15:01:53 +0200 Subject: [PATCH 19/73] ESLint: Fix warning in getBlockAttribute documentation (#46500) --- packages/blocks/src/api/parser/get-block-attributes.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/blocks/src/api/parser/get-block-attributes.js b/packages/blocks/src/api/parser/get-block-attributes.js index c8d7aa88204cca..ffa67c11250c2f 100644 --- a/packages/blocks/src/api/parser/get-block-attributes.js +++ b/packages/blocks/src/api/parser/get-block-attributes.js @@ -102,11 +102,11 @@ export function isOfTypes( value, types ) { * commentAttributes returns the attribute value depending on its source * definition of the given attribute key. * - * @param {string} attributeKey Attribute key. - * @param {Object} attributeSchema Attribute's schema. - * @param {Node} innerDOM Parsed DOM of block's inner HTML. - * @param {Object} commentAttributes Block's comment attributes. - * @param {string} innerHTML Raw HTML from block node's innerHTML property. + * @param {string} attributeKey Attribute key. + * @param {Object} attributeSchema Attribute's schema. + * @param {Node} innerDOM Parsed DOM of block's inner HTML. + * @param {Object} commentAttributes Block's comment attributes. + * @param {string} innerHTML Raw HTML from block node's innerHTML property. * * @return {*} Attribute value. */ From 6a54882a9b0814e424897dc2e334986c3bcc887e Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Tue, 13 Dec 2022 15:57:03 +0200 Subject: [PATCH 20/73] Components: Also ignore no-node-access for some components (#46501) --- packages/components/src/range-control/test/index.tsx | 4 ++-- packages/components/src/tooltip/test/index.js | 4 ++-- packages/components/src/unit-control/test/index.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/components/src/range-control/test/index.tsx b/packages/components/src/range-control/test/index.tsx index 0343b084daaa41..bbb97dbfaa0859 100644 --- a/packages/components/src/range-control/test/index.tsx +++ b/packages/components/src/range-control/test/index.tsx @@ -43,11 +43,11 @@ describe( 'RangeControl', () => { /> ); - // eslint-disable-next-line testing-library/no-container + // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access const beforeIcon = container.querySelector( '.dashicons-format-image' ); - // eslint-disable-next-line testing-library/no-container + // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access const afterIcon = container.querySelector( '.dashicons-format-video' ); diff --git a/packages/components/src/tooltip/test/index.js b/packages/components/src/tooltip/test/index.js index 9e827c6172b15d..21892dea6a0da5 100644 --- a/packages/components/src/tooltip/test/index.js +++ b/packages/components/src/tooltip/test/index.js @@ -212,7 +212,7 @@ describe( 'Tooltip', () => { // Note: this is testing for implementation details, // but couldn't find a better way. const buttonRect = button.getBoundingClientRect(); - // eslint-disable-next-line testing-library/no-container + // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access const eventCatcher = container.querySelector( '.event-catcher' ); const eventCatcherRect = eventCatcher.getBoundingClientRect(); expect( buttonRect ).toEqual( eventCatcherRect ); @@ -252,7 +252,7 @@ describe( 'Tooltip', () => { ); - // eslint-disable-next-line testing-library/no-container + // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access const eventCatcher = container.querySelector( '.event-catcher' ); await user.click( eventCatcher ); expect( onClickMock ).not.toHaveBeenCalled(); diff --git a/packages/components/src/unit-control/test/index.tsx b/packages/components/src/unit-control/test/index.tsx index 32e565fec23866..1703b3ad34db4e 100644 --- a/packages/components/src/unit-control/test/index.tsx +++ b/packages/components/src/unit-control/test/index.tsx @@ -115,7 +115,7 @@ describe( 'UnitControl', () => { withoutClassName.querySelector( '.components-unit-control' ) ).not.toHaveClass( 'hello' ); expect( - // eslint-disable-next-line testing-library/no-container + // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access withClassName.querySelector( '.components-unit-control' ) ).toHaveClass( 'hello' ); } ); From bfb7ed28e6ed1eecb71af029288fbbb62e13994c Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Mon, 12 Dec 2022 18:40:23 -0800 Subject: [PATCH 21/73] Check for NULL value in block_type asset handles --- lib/compat/wordpress-6.2/script-loader.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 84681b31512386..edeccf9c761ee7 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -92,13 +92,13 @@ function gutenberg_resolve_assets_override() { foreach ( $block_registry->get_all_registered() as $block_type ) { $style_handles = array_merge( $style_handles, - $block_type->style_handles, - $block_type->editor_style_handles + is_null( $block_type->style_handles ) ? array() : $block_type->style_handles, + is_null( $block_type->editor_style_handles ) ? array() : $block_type->editor_style_handles ); $script_handles = array_merge( $script_handles, - $block_type->script_handles + is_null( $block_type->script_handles ) ? array() : $block_type->script_handles ); } From 737325919dec3e244b9bfcfbb5601a07fa696933 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 13 Dec 2022 12:47:41 -0800 Subject: [PATCH 22/73] Use isset instead of is_null --- lib/compat/wordpress-6.2/script-loader.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index edeccf9c761ee7..564757dffc42ca 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -92,13 +92,13 @@ function gutenberg_resolve_assets_override() { foreach ( $block_registry->get_all_registered() as $block_type ) { $style_handles = array_merge( $style_handles, - is_null( $block_type->style_handles ) ? array() : $block_type->style_handles, - is_null( $block_type->editor_style_handles ) ? array() : $block_type->editor_style_handles + isset( $block_type->style_handles ) ? $block_type->style_handles : array(), + isset( $block_type->editor_style_handles ) ? $block_type->editor_style_handles : array() ); $script_handles = array_merge( $script_handles, - is_null( $block_type->script_handles ) ? array() : $block_type->script_handles + isset( $block_type->script_handles ) ? $block_type->script_handles : array() ); } From 4c6cfc5753bb06a9c2eceb7be2b3e2efb85a8953 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 13 Dec 2022 12:51:59 -0800 Subject: [PATCH 23/73] Use isset in if-statement instead of ternary --- lib/compat/wordpress-6.2/script-loader.php | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 564757dffc42ca..76b07970b1e3ae 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -90,16 +90,20 @@ function gutenberg_resolve_assets_override() { $block_registry = WP_Block_Type_Registry::get_instance(); foreach ( $block_registry->get_all_registered() as $block_type ) { - $style_handles = array_merge( - $style_handles, - isset( $block_type->style_handles ) ? $block_type->style_handles : array(), - isset( $block_type->editor_style_handles ) ? $block_type->editor_style_handles : array() - ); - - $script_handles = array_merge( - $script_handles, - isset( $block_type->script_handles ) ? $block_type->script_handles : array() - ); + if ( isset( $block_type->style_handles ) && isset( $block_type->editor_style_handles ) ) { + $style_handles = array_merge( + $style_handles, + $block_type->style_handles, + $block_type->editor_style_handles + ); + } + + if ( isset( $block_type->script_handles ) ) { + array_merge( + $script_handles, + $block_type->script_handles + ); + } } $style_handles = array_unique( $style_handles ); From 4b4a3a049e6c7351f39b3350ca851ac08040cbb7 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 13 Dec 2022 12:52:26 -0800 Subject: [PATCH 24/73] Add comment --- lib/compat/wordpress-6.2/script-loader.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 76b07970b1e3ae..99699b29069299 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -90,6 +90,7 @@ function gutenberg_resolve_assets_override() { $block_registry = WP_Block_Type_Registry::get_instance(); foreach ( $block_registry->get_all_registered() as $block_type ) { + // In older WordPress versions, like 6.0, these properties are not defined. if ( isset( $block_type->style_handles ) && isset( $block_type->editor_style_handles ) ) { $style_handles = array_merge( $style_handles, From b0fbafcac9724767a401a96e241159cf96dad5fc Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 13 Dec 2022 16:28:33 -0800 Subject: [PATCH 25/73] Add is_array check --- lib/compat/wordpress-6.2/script-loader.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 99699b29069299..5180b9074ea57b 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -91,7 +91,12 @@ function gutenberg_resolve_assets_override() { foreach ( $block_registry->get_all_registered() as $block_type ) { // In older WordPress versions, like 6.0, these properties are not defined. - if ( isset( $block_type->style_handles ) && isset( $block_type->editor_style_handles ) ) { + if ( + isset( $block_type->style_handles ) && + is_array($block_type->style_handles) && + isset( $block_type->editor_style_handles ) && + is_array( $block_type->editor_style_handles ) + ) { $style_handles = array_merge( $style_handles, $block_type->style_handles, @@ -99,7 +104,7 @@ function gutenberg_resolve_assets_override() { ); } - if ( isset( $block_type->script_handles ) ) { + if ( isset( $block_type->script_handles ) && is_array( $block_type->script_handles ) ) { array_merge( $script_handles, $block_type->script_handles From 6b57588b6c47f093a2b406e70bd0b5cbe9d9f3e4 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 13 Dec 2022 17:12:58 -0800 Subject: [PATCH 26/73] Don't block merging both if only one is unavailable --- lib/compat/wordpress-6.2/script-loader.php | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index 5180b9074ea57b..d8f66c72a8dacb 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -91,24 +91,16 @@ function gutenberg_resolve_assets_override() { foreach ( $block_registry->get_all_registered() as $block_type ) { // In older WordPress versions, like 6.0, these properties are not defined. - if ( - isset( $block_type->style_handles ) && - is_array($block_type->style_handles) && - isset( $block_type->editor_style_handles ) && - is_array( $block_type->editor_style_handles ) - ) { - $style_handles = array_merge( - $style_handles, - $block_type->style_handles, - $block_type->editor_style_handles - ); + if ( isset( $block_type->style_handles ) && is_array( $block_type->style_handles ) ) { + array_merge( $style_handles, $block_type->style_handles ); + } + + if ( isset( $block_type->editor_style_handles ) && is_array( $block_type->editor_style_handles ) ) { + array_merge( $style_handles, $block_type->editor_style_handles ); } if ( isset( $block_type->script_handles ) && is_array( $block_type->script_handles ) ) { - array_merge( - $script_handles, - $block_type->script_handles - ); + array_merge( $script_handles, $block_type->script_handles ); } } From e60ecece4245d357f50c5437f28f1b47ec583ec7 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 13 Dec 2022 17:34:59 -0800 Subject: [PATCH 27/73] Add variable assignment back --- lib/compat/wordpress-6.2/script-loader.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compat/wordpress-6.2/script-loader.php b/lib/compat/wordpress-6.2/script-loader.php index d8f66c72a8dacb..e08bfa6e46ca8e 100644 --- a/lib/compat/wordpress-6.2/script-loader.php +++ b/lib/compat/wordpress-6.2/script-loader.php @@ -92,15 +92,15 @@ function gutenberg_resolve_assets_override() { foreach ( $block_registry->get_all_registered() as $block_type ) { // In older WordPress versions, like 6.0, these properties are not defined. if ( isset( $block_type->style_handles ) && is_array( $block_type->style_handles ) ) { - array_merge( $style_handles, $block_type->style_handles ); + $style_handles = array_merge( $style_handles, $block_type->style_handles ); } if ( isset( $block_type->editor_style_handles ) && is_array( $block_type->editor_style_handles ) ) { - array_merge( $style_handles, $block_type->editor_style_handles ); + $style_handles = array_merge( $style_handles, $block_type->editor_style_handles ); } if ( isset( $block_type->script_handles ) && is_array( $block_type->script_handles ) ) { - array_merge( $script_handles, $block_type->script_handles ); + $script_handles = array_merge( $script_handles, $block_type->script_handles ); } } From da7e4c25789498b0cf3c9a4d0467af46c05bd81e Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 14 Dec 2022 02:15:23 +0000 Subject: [PATCH 28/73] Bump plugin version to 14.7.2 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index c5f94c50522dbd..e5991644a8bc27 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the new block editor in core. * Requires at least: 6.0 * Requires PHP: 5.6 - * Version: 14.7.1 + * Version: 14.7.2 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index bb28f83b398d20..cd8f7e75faa8c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "14.7.1", + "version": "14.7.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 386b7b6314b0fd..5da177ab8d6611 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "14.7.1", + "version": "14.7.2", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From d7199fc5b75d5d2895c0981b0046ce0aeb723bc3 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 14 Dec 2022 02:24:43 +0000 Subject: [PATCH 29/73] Update Changelog for 14.7.2 --- changelog.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/changelog.txt b/changelog.txt index 5427245e8d4688..d6c423dae8f803 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,29 @@ == Changelog == += 14.7.2 = + + + +## Changelog + +### Bug Fixes + +- Fix fatal error when using the plugin with PHP 8 and WordPress 6.0 by checking if block_type asset properties are set. ([46488](https://github.com/WordPress/gutenberg/pull/46488)) + + +## First time contributors + +The following PRs were merged by first time contributors: + + + +## Contributors + +The following contributors merged PRs in this release: + +@noahtallen + + = 14.7.1 = From 87b21a557661ff53fd93ea112ae7e70e65da3382 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 14 Dec 2022 10:34:09 +0800 Subject: [PATCH 30/73] Ensure drag handle matches cursor position when resizing a center aligned image (#46497) --- packages/block-library/src/image/image.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 37a8d25d1f9fa2..5eb511b455864d 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -611,6 +611,7 @@ export default function Image( { height: parseInt( currentHeight + delta.height, 10 ), } ); } } + resizeRatio={ align === 'center' ? 2 : 1 } > { img } From 826034588bca5a127fc56e49f5cb734d21dc1d1d Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 14 Dec 2022 10:04:25 +0200 Subject: [PATCH 31/73] ESLint: Fix warnings in the data package (#46499) --- packages/data/src/plugins/persistence/test/index.js | 7 ++++--- packages/data/src/test/registry.js | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/data/src/plugins/persistence/test/index.js b/packages/data/src/plugins/persistence/test/index.js index 818f075640ae56..e73af963bbeca9 100644 --- a/packages/data/src/plugins/persistence/test/index.js +++ b/packages/data/src/plugins/persistence/test/index.js @@ -26,9 +26,10 @@ describe( 'persistence', () => { } ); it( 'should not mutate options', () => { - const options = Object.freeze( { persist: true, reducer() {} } ); - - registry.registerStore( 'test', options ); + expect( () => { + const options = Object.freeze( { persist: true, reducer() {} } ); + registry.registerStore( 'test', options ); + } ).not.toThrowError( /object is not extensible/ ); } ); it( 'should load a persisted value as initialState', () => { diff --git a/packages/data/src/test/registry.js b/packages/data/src/test/registry.js index 8f73b3e442900c..a40d7628d5dc2b 100644 --- a/packages/data/src/test/registry.js +++ b/packages/data/src/test/registry.js @@ -1,3 +1,5 @@ +/* eslint jest/expect-expect: ["warn", { "assertFunctionNames": ["expect", "subscribeUntil"] }] */ + /** * Internal dependencies */ From a3db4171488fe7a89ec5ed4828f62413e3eb18f2 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 14 Dec 2022 13:05:27 +0400 Subject: [PATCH 32/73] Site Editor: Keep edited entity in sync when Editor canvas isn't mounted (#46524) --- packages/edit-site/src/components/editor/index.js | 4 ---- packages/edit-site/src/components/layout/index.js | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index b9ce354f58159b..2f2f6264f2e8d8 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -31,7 +31,6 @@ import { SidebarComplementaryAreaFills } from '../sidebar-edit-mode'; import BlockEditor from '../block-editor'; import CodeEditor from '../code-editor'; import KeyboardShortcuts from '../keyboard-shortcuts'; -import useInitEditedEntityFromURL from '../sync-state-with-url/use-init-edited-entity-from-url'; import InserterSidebar from '../secondary-sidebar/inserter-sidebar'; import ListViewSidebar from '../secondary-sidebar/list-view-sidebar'; import WelcomeGuide from '../welcome-guide'; @@ -52,9 +51,6 @@ const interfaceLabels = { }; export default function Editor() { - // This ensures the edited entity id and type are initialized properly. - useInitEditedEntityFromURL(); - const { editedPostId, editedPostType, diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index ab3d90eabb83a1..75040bbb9c466a 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -36,10 +36,14 @@ import getIsListPage from '../../utils/get-is-list-page'; import Header from '../header-edit-mode'; import SiteIcon from '../site-icon'; import SiteTitle from '../site-title'; +import useInitEditedEntityFromURL from '../sync-state-with-url/use-init-edited-entity-from-url'; const ANIMATION_DURATION = 0.5; export default function Layout( { onError } ) { + // This ensures the edited entity id and type are initialized properly. + useInitEditedEntityFromURL(); + const { params } = useLocation(); const isListPage = getIsListPage( params ); const isEditorPage = ! isListPage; From 1d62d0ddfb1403b71ca28cabdf9d818f6c4b990f Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 14 Dec 2022 20:21:44 +0800 Subject: [PATCH 33/73] Query: Add migration of colors to v2 deprecation (#46522) --- .../block-library/src/query/deprecated.js | 188 +++++++++--------- ...core__query__deprecated-2-with-colors.html | 6 + ...core__query__deprecated-2-with-colors.json | 72 +++++++ ...uery__deprecated-2-with-colors.parsed.json | 59 ++++++ ...__deprecated-2-with-colors.serialized.html | 7 + 5 files changed, 238 insertions(+), 94 deletions(-) create mode 100644 test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.html create mode 100644 test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.json create mode 100644 test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.parsed.json create mode 100644 test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.serialized.html diff --git a/packages/block-library/src/query/deprecated.js b/packages/block-library/src/query/deprecated.js index d38ab3328559be..b23455d50489b5 100644 --- a/packages/block-library/src/query/deprecated.js +++ b/packages/block-library/src/query/deprecated.js @@ -31,6 +31,95 @@ const migrateToTaxQuery = ( attributes ) => { }; }; +const migrateColors = ( attributes, innerBlocks ) => { + // Remove color style attributes from the Query block. + const { style, backgroundColor, gradient, textColor, ...newAttributes } = + attributes; + + const hasColorStyles = + backgroundColor || + gradient || + textColor || + style?.color || + style?.elements?.link; + + // If the query block doesn't currently have any color styles, + // nothing needs migrating. + if ( ! hasColorStyles ) { + return [ attributes, innerBlocks ]; + } + + // Clean color values from style attribute object. + if ( style ) { + newAttributes.style = cleanEmptyObject( { + ...style, + color: undefined, + elements: { + ...style.elements, + link: undefined, + }, + } ); + } + + // If the inner blocks are already wrapped in a single group + // block, add the color support styles to that group block. + if ( hasSingleInnerGroupBlock( innerBlocks ) ) { + const groupBlock = innerBlocks[ 0 ]; + + // Create new styles for the group block. + const hasStyles = + style?.color || + style?.elements?.link || + groupBlock.attributes.style; + + const newStyles = hasStyles + ? cleanEmptyObject( { + ...groupBlock.attributes.style, + color: style?.color, + elements: style?.elements?.link + ? { link: style?.elements?.link } + : undefined, + } ) + : undefined; + + // Create a new Group block from the original. + const updatedGroupBlock = createBlock( + 'core/group', + { + ...groupBlock.attributes, + backgroundColor, + gradient, + textColor, + style: newStyles, + }, + groupBlock.innerBlocks + ); + + return [ newAttributes, [ updatedGroupBlock ] ]; + } + + // When we don't have a single wrapping group block for the inner + // blocks, wrap the current inner blocks in a group applying the + // color styles to that. + const newGroupBlock = createBlock( + 'core/group', + { + backgroundColor, + gradient, + textColor, + style: cleanEmptyObject( { + color: style?.color, + elements: style?.elements?.link + ? { link: style?.elements?.link } + : undefined, + } ), + }, + innerBlocks + ); + + return [ newAttributes, [ newGroupBlock ] ]; +}; + const hasSingleInnerGroupBlock = ( innerBlocks = [] ) => innerBlocks.length === 1 && innerBlocks[ 0 ].name === 'core/group'; @@ -127,7 +216,10 @@ const v2 = { }, isEligible: ( { query: { categoryIds, tagIds } = {} } ) => categoryIds || tagIds, - migrate: migrateToTaxQuery, + migrate( attributes, innerBlocks ) { + const withTaxQuery = migrateToTaxQuery( attributes ); + return migrateColors( withTaxQuery, innerBlocks ); + }, save( { attributes: { tagName: Tag = 'div' } } ) { const blockProps = useBlockProps.save(); const innerBlocksProps = useInnerBlocksProps.save( blockProps ); @@ -196,99 +288,7 @@ const v3 = { style?.elements?.link ); }, - migrate: ( attributes, innerBlocks ) => { - // Remove color style attributes from the Query block. - const { - style, - backgroundColor, - gradient, - textColor, - ...newAttributes - } = attributes; - - const hasColorStyles = - backgroundColor || - gradient || - textColor || - style?.color || - style?.elements?.link; - - // If the query block doesn't currently have any color styles, - // nothing needs migrating. - if ( ! hasColorStyles ) { - return [ attributes, innerBlocks ]; - } - - // Clean color values from style attribute object. - if ( style ) { - newAttributes.style = cleanEmptyObject( { - ...style, - color: undefined, - elements: { - ...style.elements, - link: undefined, - }, - } ); - } - - // If the inner blocks are already wrapped in a single group - // block, add the color support styles to that group block. - if ( hasSingleInnerGroupBlock( innerBlocks ) ) { - const groupBlock = innerBlocks[ 0 ]; - - // Create new styles for the group block. - const hasStyles = - style?.color || - style?.elements?.link || - groupBlock.attributes.style; - - const newStyles = hasStyles - ? cleanEmptyObject( { - ...groupBlock.attributes.style, - color: style?.color, - elements: style?.elements?.link - ? { link: style?.elements?.link } - : undefined, - } ) - : undefined; - - // Create a new Group block from the original. - const updatedGroupBlock = createBlock( - 'core/group', - { - ...groupBlock.attributes, - backgroundColor, - gradient, - textColor, - style: newStyles, - }, - groupBlock.innerBlocks - ); - - return [ newAttributes, [ updatedGroupBlock ] ]; - } - - // When we don't have a single wrapping group block for the inner - // blocks, wrap the current inner blocks in a group applying the - // color styles to that. - const newGroupBlock = createBlock( - 'core/group', - { - backgroundColor, - gradient, - textColor, - style: cleanEmptyObject( { - color: style?.color, - elements: style?.elements?.link - ? { link: style?.elements?.link } - : undefined, - } ), - }, - innerBlocks - ); - - return [ newAttributes, [ newGroupBlock ] ]; - }, + migrate: migrateColors, save( { attributes: { tagName: Tag = 'div' } } ) { const blockProps = useBlockProps.save(); const innerBlocksProps = useInnerBlocksProps.save( blockProps ); diff --git a/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.html b/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.html new file mode 100644 index 00000000000000..e85e61b5d7b144 --- /dev/null +++ b/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.html @@ -0,0 +1,6 @@ + + + diff --git a/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.json b/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.json new file mode 100644 index 00000000000000..82bc41a40fb1b5 --- /dev/null +++ b/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.json @@ -0,0 +1,72 @@ +[ + { + "name": "core/query", + "isValid": true, + "attributes": { + "queryId": 19, + "query": { + "perPage": 3, + "pages": 0, + "offset": 0, + "postType": "post", + "order": "desc", + "orderBy": "date", + "author": "", + "search": "", + "exclude": [], + "sticky": "", + "inherit": false, + "taxQuery": { + "category": [ 3, 5 ], + "post_tag": [ 6 ] + } + }, + "tagName": "div", + "displayLayout": { + "type": "list" + } + }, + "innerBlocks": [ + { + "name": "core/group", + "isValid": true, + "attributes": { + "tagName": "div", + "textColor": "pale-cyan-blue", + "style": { + "color": { + "background": "#284d5f" + }, + "elements": { + "link": { + "color": { + "text": "var:preset|color|luminous-vivid-amber" + } + } + } + } + }, + "innerBlocks": [ + { + "name": "core/post-template", + "isValid": true, + "attributes": {}, + "innerBlocks": [ + { + "name": "core/post-title", + "isValid": true, + "attributes": { + "level": 2, + "isLink": false, + "rel": "", + "linkTarget": "_self" + }, + "innerBlocks": [] + } + ] + } + ] + } + ] + } +] diff --git a/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.parsed.json b/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.parsed.json new file mode 100644 index 00000000000000..73e4f9b6c18f4b --- /dev/null +++ b/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.parsed.json @@ -0,0 +1,59 @@ +[ + { + "blockName": "core/query", + "attrs": { + "queryId": 19, + "query": { + "perPage": 3, + "pages": 0, + "offset": 0, + "postType": "post", + "categoryIds": [ 3, 5 ], + "tagIds": [ 6 ], + "order": "desc", + "orderBy": "date", + "author": "", + "search": "", + "exclude": [], + "sticky": "", + "inherit": false + }, + "style": { + "color": { + "background": "#284d5f" + }, + "elements": { + "link": { + "color": { + "text": "var:preset|color|luminous-vivid-amber" + } + } + } + }, + "textColor": "pale-cyan-blue" + }, + "innerBlocks": [ + { + "blockName": "core/post-template", + "attrs": {}, + "innerBlocks": [ + { + "blockName": "core/post-title", + "attrs": {}, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + } + ], + "innerHTML": "\n\n", + "innerContent": [ "\n", null, "\n" ] + } + ], + "innerHTML": "\n
\n
\n", + "innerContent": [ + "\n
", + null, + "\n
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.serialized.html b/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.serialized.html new file mode 100644 index 00000000000000..f86b4f26ecc1d1 --- /dev/null +++ b/test/integration/fixtures/blocks/core__query__deprecated-2-with-colors.serialized.html @@ -0,0 +1,7 @@ + +
+ +
+ From 69872189f108abc7665b0bdaec37087c8a28c57f Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 14 Dec 2022 12:25:51 +0000 Subject: [PATCH 34/73] Add selector and test (#46531) --- .../data/data-core-block-editor.md | 12 +++++++++++ packages/block-editor/src/store/selectors.js | 10 +++++++++ .../block-editor/src/store/test/selectors.js | 21 +++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 5123dc6adc1318..0580aa0141b2be 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -556,6 +556,18 @@ _Properties_ - _isDisabled_ `boolean`: Whether or not the user should be prevented from inserting this item. - _frecency_ `number`: Heuristic that combines frequency and recency. +### getLastInsertedBlockClientId + +Gets the client id of the last inserted block. + +_Parameters_ + +- _state_ `Object`: Global application state. + +_Returns_ + +- `string|undefined`: Client Id of the last inserted block. + ### getLastMultiSelectedBlockClientId Returns the client ID of the last block in the multi-selection set, or null diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 694f54bacdee01..3c048a7d58a29c 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2653,6 +2653,16 @@ export function wasBlockJustInserted( state, clientId, source ) { ); } +/** + * Gets the client id of the last inserted block. + * + * @param {Object} state Global application state. + * @return {string|undefined} Client Id of the last inserted block. + */ +export function getLastInsertedBlockClientId( state ) { + return state?.lastBlockInserted?.clientId; +} + /** * Tells if the block is visible on the canvas or not. * diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 0086714d128044..ee4e9ee4c167ab 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -73,6 +73,7 @@ const { __experimentalGetPatternTransformItems, wasBlockJustInserted, __experimentalGetGlobalBlocksByName, + getLastInsertedBlockClientId, } = selectors; describe( 'selectors', () => { @@ -4665,3 +4666,23 @@ describe( '__unstableGetClientIdsTree', () => { ] ); } ); } ); + +describe( 'getLastInsertedBlockClientId', () => { + it( 'should return undefined if no blocks have been inserted', () => { + const state = { + lastBlockInserted: {}, + }; + + expect( getLastInsertedBlockClientId( state ) ).toEqual( undefined ); + } ); + + it( 'should return clientId if blocks have been inserted', () => { + const state = { + lastBlockInserted: { + clientId: '123456', + }, + }; + + expect( getLastInsertedBlockClientId( state ) ).toEqual( '123456' ); + } ); +} ); From a8fcabdcd177a1571d9627aee1669b2aecca0b49 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 14 Dec 2022 16:39:03 +0400 Subject: [PATCH 35/73] Site Editor: Fix the top bar 'exist' animation (#46533) --- packages/edit-site/src/components/layout/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index 75040bbb9c466a..150f2d9cce882d 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -177,7 +177,7 @@ export default function Layout( { onError } ) { as={ motion.div } initial={ { y: -60 } } animate={ { y: 0 } } - edit={ { y: -60 } } + exit={ { y: -60 } } transition={ { type: 'tween', duration: disableMotion From 40d638b09362d7fcb45d36aa6756160e74343830 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 14 Dec 2022 14:25:19 +0100 Subject: [PATCH 36/73] Fix typing performance issue for container blocks (#46527) --- .../src/components/inner-blocks/index.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/inner-blocks/index.js b/packages/block-editor/src/components/inner-blocks/index.js index 4542afc797462a..9d0b3f34bc7729 100644 --- a/packages/block-editor/src/components/inner-blocks/index.js +++ b/packages/block-editor/src/components/inner-blocks/index.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { useViewportMatch, useMergeRefs } from '@wordpress/compose'; -import { forwardRef } from '@wordpress/element'; +import { forwardRef, useMemo } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; import { getBlockSupport, @@ -102,6 +102,16 @@ function UncontrolledInnerBlocks( props ) { const { allowSizingOnChildren = false } = getBlockSupport( name, '__experimentalLayout' ) || {}; + const layout = useMemo( + () => ( { + ...__experimentalLayout, + ...( allowSizingOnChildren && { + allowSizingOnChildren: true, + } ), + } ), + [ __experimentalLayout, allowSizingOnChildren ] + ); + // This component needs to always be synchronous as it's the one changing // the async mode depending on the block selection. return ( @@ -110,12 +120,7 @@ function UncontrolledInnerBlocks( props ) { rootClientId={ clientId } renderAppender={ renderAppender } __experimentalAppenderTagName={ __experimentalAppenderTagName } - __experimentalLayout={ { - ...__experimentalLayout, - ...( allowSizingOnChildren && { - allowSizingOnChildren: true, - } ), - } } + __experimentalLayout={ layout } wrapperRef={ wrapperRef } placeholder={ placeholder } /> From ce4fa5dbe6c9a8486cde0efda486e78522ff4388 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 14 Dec 2022 13:51:17 +0000 Subject: [PATCH 37/73] Add page list to Link UI transforms in Nav block (#46426) * Initial work * Remove debugging * Allow Page List in Nav Link and Submenu * Fix introduction of bug * Remove console log --- .../src/components/off-canvas-editor/link-ui.js | 1 + packages/block-library/src/navigation-link/edit.js | 1 + .../block-library/src/navigation-link/link-ui.js | 2 ++ .../src/navigation-link/transforms.js | 14 ++++++++++++++ .../block-library/src/navigation-submenu/edit.js | 6 +++++- 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/off-canvas-editor/link-ui.js b/packages/block-editor/src/components/off-canvas-editor/link-ui.js index 000ffc54f0ec29..f122d9a1e698fe 100644 --- a/packages/block-editor/src/components/off-canvas-editor/link-ui.js +++ b/packages/block-editor/src/components/off-canvas-editor/link-ui.js @@ -75,6 +75,7 @@ function LinkControlTransforms( { clientId } ) { const { replaceBlock } = useDispatch( blockEditorStore ); const featuredBlocks = [ + 'core/page-list', 'core/site-logo', 'core/social-links', 'core/search', diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 02c96d2e5f61ef..7e58527d947f5c 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -424,6 +424,7 @@ export default function NavigationLinkEdit( { const ALLOWED_BLOCKS = [ 'core/navigation-link', 'core/navigation-submenu', + 'core/page-list', ]; const DEFAULT_BLOCK = { name: 'core/navigation-link', diff --git a/packages/block-library/src/navigation-link/link-ui.js b/packages/block-library/src/navigation-link/link-ui.js index 3c7387740e3790..a2df4f2a294055 100644 --- a/packages/block-library/src/navigation-link/link-ui.js +++ b/packages/block-library/src/navigation-link/link-ui.js @@ -74,10 +74,12 @@ function LinkControlTransforms( { clientId } ) { const { replaceBlock } = useDispatch( blockEditorStore ); const featuredBlocks = [ + 'core/page-list', 'core/site-logo', 'core/social-links', 'core/search', ]; + const transforms = blockTransforms.filter( ( item ) => { return featuredBlocks.includes( item.name ); } ); diff --git a/packages/block-library/src/navigation-link/transforms.js b/packages/block-library/src/navigation-link/transforms.js index 7b213a48051065..fb450a13a02dc4 100644 --- a/packages/block-library/src/navigation-link/transforms.js +++ b/packages/block-library/src/navigation-link/transforms.js @@ -40,6 +40,13 @@ const transforms = { return createBlock( 'core/navigation-link' ); }, }, + { + type: 'block', + blocks: [ 'core/page-list' ], + transform: () => { + return createBlock( 'core/navigation-link' ); + }, + }, ], to: [ { @@ -91,6 +98,13 @@ const transforms = { } ); }, }, + { + type: 'block', + blocks: [ 'core/page-list' ], + transform: () => { + return createBlock( 'core/page-list' ); + }, + }, ], }; diff --git a/packages/block-library/src/navigation-submenu/edit.js b/packages/block-library/src/navigation-submenu/edit.js index e4568c8d48e2df..47a222bd57bb66 100644 --- a/packages/block-library/src/navigation-submenu/edit.js +++ b/packages/block-library/src/navigation-submenu/edit.js @@ -43,7 +43,11 @@ import { name } from './block.json'; import { LinkUI } from '../navigation-link/link-ui'; import { updateAttributes } from '../navigation-link/update-attributes'; -const ALLOWED_BLOCKS = [ 'core/navigation-link', 'core/navigation-submenu' ]; +const ALLOWED_BLOCKS = [ + 'core/navigation-link', + 'core/navigation-submenu', + 'core/page-list', +]; const DEFAULT_BLOCK = { name: 'core/navigation-link', From 42aa42090b6b86da5edfde55563ce536995a3aed Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 14 Dec 2022 16:51:53 +0200 Subject: [PATCH 38/73] Lodash: Refactor edit-site away from _.find() (#46539) --- .../edit-site/src/components/global-styles/utils.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/utils.js b/packages/edit-site/src/components/global-styles/utils.js index 4c9d7bae19f7a1..06940becaf7ec1 100644 --- a/packages/edit-site/src/components/global-styles/utils.js +++ b/packages/edit-site/src/components/global-styles/utils.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { get, find } from 'lodash'; +import { get } from 'lodash'; /** * Internal dependencies @@ -120,8 +120,7 @@ function findInPresetsBy( for ( const origin of origins ) { const presets = presetByOrigin[ origin ]; if ( presets ) { - const presetObject = find( - presets, + const presetObject = presets.find( ( preset ) => preset[ presetProperty ] === presetValueValue ); @@ -164,7 +163,9 @@ export function getPresetVariableFromValue( const cssVarInfix = STYLE_PATH_TO_CSS_VAR_INFIX[ variableStylePath ]; - const metadata = find( PRESET_METADATA, [ 'cssVarInfix', cssVarInfix ] ); + const metadata = PRESET_METADATA.find( + ( data ) => data.cssVarInfix === cssVarInfix + ); if ( ! metadata ) { // The property doesn't have preset data @@ -196,7 +197,9 @@ function getValueFromPresetVariable( variable, [ presetType, slug ] ) { - const metadata = find( PRESET_METADATA, [ 'cssVarInfix', presetType ] ); + const metadata = PRESET_METADATA.find( + ( data ) => data.cssVarInfix === presetType + ); if ( ! metadata ) { return variable; } From 804d6e55a901c9034051af72925a528d2fd02ef8 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Wed, 14 Dec 2022 17:02:30 +0200 Subject: [PATCH 39/73] Lodash: Refactor block library away from _.find() (#46430) --- packages/block-library/src/gallery/edit.js | 9 ++++----- packages/block-library/src/gallery/v1/edit.js | 10 +++++----- packages/block-library/src/social-link/social-list.js | 9 ++------- .../src/template-part/edit/utils/hooks.js | 10 +++++++--- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 036f0c2133a5d3..c8d6731198c7c6 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -2,7 +2,6 @@ * External dependencies */ import classnames from 'classnames'; -import { find } from 'lodash'; /** * WordPress dependencies @@ -174,7 +173,7 @@ function GalleryEdit( props ) { */ function buildImageAttributes( imageAttributes ) { const image = imageAttributes.id - ? find( imageData, { id: imageAttributes.id } ) + ? imageData.find( ( { id } ) => id === imageAttributes.id ) : null; let newClassName; @@ -220,7 +219,7 @@ function GalleryEdit( props ) { // It's necessary to retrieve the media type from the raw image data for already-uploaded images on native. const nativeFileData = Platform.isNative && file.id - ? find( imageData, { id: file.id } ) + ? imageData.find( ( { id } ) => id === file.id ) : null; const mediaTypeSelector = nativeFileData @@ -333,7 +332,7 @@ function GalleryEdit( props ) { getBlock( clientId ).innerBlocks.forEach( ( block ) => { blocks.push( block.clientId ); const image = block.attributes.id - ? find( imageData, { id: block.attributes.id } ) + ? imageData.find( ( { id } ) => id === block.attributes.id ) : null; changedAttributes[ block.clientId ] = getHrefAndDestination( image, @@ -401,7 +400,7 @@ function GalleryEdit( props ) { getBlock( clientId ).innerBlocks.forEach( ( block ) => { blocks.push( block.clientId ); const image = block.attributes.id - ? find( imageData, { id: block.attributes.id } ) + ? imageData.find( ( { id } ) => id === block.attributes.id ) : null; changedAttributes[ block.clientId ] = getImageSizeAttributes( image, diff --git a/packages/block-library/src/gallery/v1/edit.js b/packages/block-library/src/gallery/v1/edit.js index e79760a544cd2c..9c36f02ec028ad 100644 --- a/packages/block-library/src/gallery/v1/edit.js +++ b/packages/block-library/src/gallery/v1/edit.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { find, get, isEmpty, map } from 'lodash'; +import { get, isEmpty, map } from 'lodash'; /** * WordPress dependencies @@ -211,7 +211,7 @@ function GalleryEdit( props ) { // string, so ensure comparison works correctly by converting the // newImage.id to a string. const newImageId = newImage.id.toString(); - const currentImage = find( images, { id: newImageId } ); + const currentImage = images.find( ( { id } ) => id === newImageId ); const currentImageCaption = currentImage ? currentImage.caption : newImage.caption; @@ -220,9 +220,9 @@ function GalleryEdit( props ) { return currentImageCaption; } - const attachment = find( attachmentCaptions, { - id: newImageId, - } ); + const attachment = attachmentCaptions.find( + ( { id } ) => id === newImageId + ); // If the attachment caption is updated. if ( attachment && attachment.caption !== newImage.caption ) { diff --git a/packages/block-library/src/social-link/social-list.js b/packages/block-library/src/social-link/social-list.js index 9ac527dccd0d39..38c1ef91f99384 100644 --- a/packages/block-library/src/social-link/social-list.js +++ b/packages/block-library/src/social-link/social-list.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { find } from 'lodash'; - /** * WordPress dependencies */ @@ -22,7 +17,7 @@ import { ChainIcon } from './icons'; * @return {WPComponent} Icon component for social service. */ export const getIconBySite = ( name ) => { - const variation = find( variations, { name } ); + const variation = variations.find( ( v ) => v.name === name ); return variation ? variation.icon : ChainIcon; }; @@ -34,6 +29,6 @@ export const getIconBySite = ( name ) => { * @return {string} Display name for social service */ export const getNameBySite = ( name ) => { - const variation = find( variations, { name } ); + const variation = variations.find( ( v ) => v.name === name ); return variation ? variation.title : __( 'Social Icon' ); }; diff --git a/packages/block-library/src/template-part/edit/utils/hooks.js b/packages/block-library/src/template-part/edit/utils/hooks.js index e5b60131d84ee2..37eb762a24144a 100644 --- a/packages/block-library/src/template-part/edit/utils/hooks.js +++ b/packages/block-library/src/template-part/edit/utils/hooks.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { find, kebabCase } from 'lodash'; +import { kebabCase } from 'lodash'; /** * WordPress dependencies @@ -150,8 +150,12 @@ export function useTemplatePartArea( area ) { ).__experimentalGetDefaultTemplatePartAreas(); /* eslint-enable @wordpress/data-no-store-string-literals */ - const selectedArea = find( definedAreas, { area } ); - const defaultArea = find( definedAreas, { area: 'uncategorized' } ); + const selectedArea = definedAreas.find( + ( definedArea ) => definedArea.area === area + ); + const defaultArea = definedAreas.find( + ( definedArea ) => definedArea.area === 'uncategorized' + ); return { icon: selectedArea?.icon || defaultArea?.icon, From 3c5197cb87e1afc4fd6b24ad175724411b8ee269 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 14 Dec 2022 15:49:30 +0000 Subject: [PATCH 40/73] Bump plugin version to 14.7.3 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index e5991644a8bc27..5807424888331d 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the new block editor in core. * Requires at least: 6.0 * Requires PHP: 5.6 - * Version: 14.7.2 + * Version: 14.7.3 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index cd8f7e75faa8c2..37611b69363436 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "14.7.2", + "version": "14.7.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5da177ab8d6611..28d648a1174b9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "14.7.2", + "version": "14.7.3", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From 3245d1e0518d6a2fa565266981fe0fab8a984a8e Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Wed, 14 Dec 2022 17:02:24 +0100 Subject: [PATCH 41/73] [Mobile] - Disable Unsupported Block Editor Tests (Android) (#46542) * Mobile - Disable Unsupported Block Editor Tests * Mobile - Enable Gutenberg Editor Unsupported Block Editor Tests for iOS only --- .../gutenberg-editor-unsupported-blocks.test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js index d75b7ba4efd1e5..a5520ac92a468a 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js @@ -2,8 +2,12 @@ * Internal dependencies */ import testData from './helpers/test-data'; +import { isAndroid } from './helpers/utils'; -describe( 'Gutenberg Editor Unsupported Block Editor Tests', () => { +// Disabled for now on Android see https://github.com/wordpress-mobile/gutenberg-mobile/issues/5321 +const onlyOniOS = ! isAndroid() ? describe : describe.skip; + +onlyOniOS( 'Gutenberg Editor Unsupported Block Editor Tests', () => { it( 'should be able to open the unsupported block web view editor', async () => { await editorPage.setHtmlContent( testData.unsupportedBlockHtml ); From ee7d5ec02f185cf4bc26a8c5d09d0cd283173955 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 14 Dec 2022 16:19:13 +0000 Subject: [PATCH 42/73] Update Changelog for 14.7.3 --- changelog.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/changelog.txt b/changelog.txt index d6c423dae8f803..2d52f25c1bf8a6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,20 @@ == Changelog == += 14.7.3 = + +## Changelog + +### Bug fixes + +- Fix typing performance issue for container blocks. ([46527](https://github.com/WordPress/gutenberg/pull/46527)) + +## Contributors + +The following contributors merged PRs in this release: + +@youknowriad + + = 14.7.2 = From 0e087ff7f8ab21d8cf4a8dbfd8459d24a19edd03 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 14 Dec 2022 16:39:02 +0000 Subject: [PATCH 43/73] Bump plugin version to 14.8.0-rc.1 --- gutenberg.php | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index 5807424888331d..4d4489ef4c0615 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the new block editor in core. * Requires at least: 6.0 * Requires PHP: 5.6 - * Version: 14.7.3 + * Version: 14.8.0-rc.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 37611b69363436..66631f6f478042 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "14.7.3", + "version": "14.8.0-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 28d648a1174b9d..b1ebbf430f35fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "14.7.3", + "version": "14.8.0-rc.1", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From a1c6a8d151a68169f9012a1be9a528e764cd5601 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Wed, 14 Dec 2022 16:41:03 +0000 Subject: [PATCH 44/73] Update Nav OffCanvas Editor to use clientids tree (#46541) * Navigation: Pass a tree of client ids instead of an array of blocks. Co-authored-by: Daniel Richards <677833+talldan@users.noreply.github.com> * Explain passing custom clientIDs Co-authored-by: Ben Dwyer Co-authored-by: Daniel Richards <677833+talldan@users.noreply.github.com> Co-authored-by: Dave Smith --- .../src/components/off-canvas-editor/block.js | 2 +- .../block-library/src/navigation/edit/index.js | 6 +++--- .../navigation/edit/menu-inspector-controls.js | 16 ++++++++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/off-canvas-editor/block.js b/packages/block-editor/src/components/off-canvas-editor/block.js index af74873e1b41af..49ce5788dfafe0 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block.js +++ b/packages/block-editor/src/components/off-canvas-editor/block.js @@ -145,7 +145,7 @@ function ListViewBlock( { const { isTreeGridMounted, expand, collapse } = useListViewContext(); - const isEditable = block.name !== 'core/page-list-item'; + const isEditable = blockName !== 'core/page-list-item'; const hasSiblings = siblingBlockCount > 0; const hasRenderedMovers = showBlockMovers && hasSiblings; const moverCellClassName = classnames( diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index b414409791cbfb..d94e0022d15ea3 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -717,13 +717,13 @@ function Navigation( { return ( { + const { __unstableGetClientIdsTree } = select( blockEditorStore ); + return __unstableGetClientIdsTree( clientId ); + }, + [ clientId ] + ); + return ( { isOffCanvasNavigationEditorEnabled ? ( From 0944f75ee33fe77e58c82e31d85c2759c715081e Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Wed, 14 Dec 2022 17:23:43 +0000 Subject: [PATCH 45/73] Nav offcanvas - handle non-direct insert block inserter (#46503) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Pass blocks from block inserter to custom appender callback * Allow only blocks which support Link UI to trigger it’s rendering on insertion * Allow Link UI for all supporting blocks * Extract clearly named function to improve comprehension * Allow for programmatic disabling of selecting block on insertion via Quick Inserter * Fix stray false * Provide correct default to ensure behaviour is backwards compatible * Always pass selectBlockOnInsert --- .../inserter/hooks/use-insertion-point.js | 5 +- .../src/components/inserter/index.js | 17 ++++++- .../src/components/inserter/quick-inserter.js | 3 ++ .../src/components/inserter/search-results.js | 2 + .../components/off-canvas-editor/appender.js | 50 +++++++++++++------ 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js index d2bbaa909d0a29..52c7f9ba23f83e 100644 --- a/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js +++ b/packages/block-editor/src/components/inserter/hooks/use-insertion-point.js @@ -39,6 +39,7 @@ function useInsertionPoint( { isAppender, onSelect, shouldFocusBlock = true, + selectBlockOnInsert = true, } ) { const { getSelectedBlock } = useSelect( blockEditorStore ); const { destinationRootClientId, destinationIndex } = useSelect( @@ -108,7 +109,7 @@ function useInsertionPoint( { blocks, destinationIndex, destinationRootClientId, - true, + selectBlockOnInsert, shouldFocusBlock || shouldForceFocusBlock ? 0 : null, meta ); @@ -122,7 +123,7 @@ function useInsertionPoint( { speak( message ); if ( onSelect ) { - onSelect(); + onSelect( blocks ); } }, [ diff --git a/packages/block-editor/src/components/inserter/index.js b/packages/block-editor/src/components/inserter/index.js index 59c04e24d2bbb4..258faff2b826ab 100644 --- a/packages/block-editor/src/components/inserter/index.js +++ b/packages/block-editor/src/components/inserter/index.js @@ -143,18 +143,31 @@ class Inserter extends Component { // Feel free to make them stable after a few releases. __experimentalIsQuick: isQuick, prioritizePatterns, + onSelectOrClose, + selectBlockOnInsert, } = this.props; if ( isQuick ) { return ( { + onSelect={ ( blocks ) => { + const firstBlock = + Array.isArray( blocks ) && blocks?.length + ? blocks[ 0 ] + : blocks; + if ( + onSelectOrClose && + typeof onSelectOrClose === 'function' + ) { + onSelectOrClose( firstBlock ); + } onClose(); } } rootClientId={ rootClientId } clientId={ clientId } isAppender={ isAppender } prioritizePatterns={ prioritizePatterns } + selectBlockOnInsert={ selectBlockOnInsert } /> ); } @@ -380,7 +393,7 @@ export default compose( [ if ( onSelectOrClose ) { onSelectOrClose( { - insertedBlockId: blockToInsert?.clientId, + clientId: blockToInsert?.clientId, } ); } diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js index 39a5ac5c75b849..540b51a4757e0d 100644 --- a/packages/block-editor/src/components/inserter/quick-inserter.js +++ b/packages/block-editor/src/components/inserter/quick-inserter.js @@ -31,6 +31,7 @@ export default function QuickInserter( { clientId, isAppender, prioritizePatterns, + selectBlockOnInsert, } ) { const [ filterValue, setFilterValue ] = useState( '' ); const [ destinationRootClientId, onInsertBlocks ] = useInsertionPoint( { @@ -38,6 +39,7 @@ export default function QuickInserter( { rootClientId, clientId, isAppender, + selectBlockOnInsert, } ); const [ blockTypes ] = useBlockTypesState( destinationRootClientId, @@ -121,6 +123,7 @@ export default function QuickInserter( { maxBlockTypes={ SHOWN_BLOCK_TYPES } isDraggable={ false } prioritizePatterns={ prioritizePatterns } + selectBlockOnInsert={ selectBlockOnInsert } />
diff --git a/packages/block-editor/src/components/inserter/search-results.js b/packages/block-editor/src/components/inserter/search-results.js index dfd7a3d73312d5..f55e49bd1cc80f 100644 --- a/packages/block-editor/src/components/inserter/search-results.js +++ b/packages/block-editor/src/components/inserter/search-results.js @@ -50,6 +50,7 @@ function InserterSearchResults( { isDraggable = true, shouldFocusBlock = true, prioritizePatterns, + selectBlockOnInsert, } ) { const debouncedSpeak = useDebounce( speak, 500 ); @@ -60,6 +61,7 @@ function InserterSearchResults( { isAppender, insertionIndex: __experimentalInsertionIndex, shouldFocusBlock, + selectBlockOnInsert, } ); const [ blockTypes, diff --git a/packages/block-editor/src/components/off-canvas-editor/appender.js b/packages/block-editor/src/components/off-canvas-editor/appender.js index 646700143cf511..0fb19df664f016 100644 --- a/packages/block-editor/src/components/off-canvas-editor/appender.js +++ b/packages/block-editor/src/components/off-canvas-editor/appender.js @@ -11,8 +11,13 @@ import Inserter from '../inserter'; import { LinkUI } from './link-ui'; import { updateAttributes } from './update-attributes'; +const BLOCKS_WITH_LINK_UI_SUPPORT = [ + 'core/navigation-link', + 'core/navigation-submenu', +]; + export const Appender = forwardRef( ( props, ref ) => { - const [ insertedBlock, setInsertedBlock ] = useState(); + const [ insertedBlockClientId, setInsertedBlockClientId ] = useState(); const { hideInserter, clientId } = useSelect( ( select ) => { const { @@ -31,40 +36,55 @@ export const Appender = forwardRef( ( props, ref ) => { }; }, [] ); - const { insertedBlockAttributes } = useSelect( + const { insertedBlockAttributes, insertedBlockName } = useSelect( ( select ) => { - const { getBlockAttributes } = select( blockEditorStore ); + const { getBlockName, getBlockAttributes } = + select( blockEditorStore ); return { - insertedBlockAttributes: getBlockAttributes( insertedBlock ), + insertedBlockAttributes: getBlockAttributes( + insertedBlockClientId + ), + insertedBlockName: getBlockName( insertedBlockClientId ), }; }, - [ insertedBlock ] + [ insertedBlockClientId ] ); const { updateBlockAttributes } = useDispatch( blockEditorStore ); const setAttributes = - ( insertedBlockClientId ) => ( _updatedAttributes ) => { - updateBlockAttributes( insertedBlockClientId, _updatedAttributes ); + ( _insertedBlockClientId ) => ( _updatedAttributes ) => { + updateBlockAttributes( _insertedBlockClientId, _updatedAttributes ); }; + const maybeSetInsertedBlockOnInsertion = ( _insertedBlock ) => { + if ( ! _insertedBlock?.clientId ) { + return; + } + + setInsertedBlockClientId( _insertedBlock?.clientId ); + }; + let maybeLinkUI; - if ( insertedBlock ) { + if ( + insertedBlockClientId && + BLOCKS_WITH_LINK_UI_SUPPORT?.includes( insertedBlockName ) + ) { maybeLinkUI = ( setInsertedBlock( null ) } + onClose={ () => setInsertedBlockClientId( null ) } hasCreateSuggestion={ false } onChange={ ( updatedValue ) => { updateAttributes( updatedValue, - setAttributes( insertedBlock ), + setAttributes( insertedBlockClientId ), insertedBlockAttributes ); - setInsertedBlock( null ); + setInsertedBlockClientId( null ); } } /> ); @@ -77,15 +97,15 @@ export const Appender = forwardRef( ( props, ref ) => { return (
{ maybeLinkUI } + { - setInsertedBlock( insertedBlockId ); - } } + onSelectOrClose={ maybeSetInsertedBlockOnInsertion } + __experimentalIsQuick { ...props } />
From 04d69dde75a6c6a305b5b89c4adf2621f7b9c600 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Wed, 14 Dec 2022 17:30:42 +0000 Subject: [PATCH 46/73] Page List: Update the icon to demonstrate that the pages are automatically updated (#46438) * Page List: Update the icon to demonstrate that the pages are automatically updated * Allow a context to be passed to the block icon --- .../block-editor/src/components/block-icon/index.js | 6 ++++-- .../off-canvas-editor/block-select-button.js | 6 +++++- packages/block-library/src/page-list/index.js | 10 ++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/block-editor/src/components/block-icon/index.js b/packages/block-editor/src/components/block-icon/index.js index ad3484c9e4a049..58d7b3a04adb9e 100644 --- a/packages/block-editor/src/components/block-icon/index.js +++ b/packages/block-editor/src/components/block-icon/index.js @@ -10,14 +10,16 @@ import { Icon } from '@wordpress/components'; import { blockDefault } from '@wordpress/icons'; import { memo } from '@wordpress/element'; -function BlockIcon( { icon, showColors = false, className } ) { +function BlockIcon( { icon, showColors = false, className, context } ) { if ( icon?.src === 'block-default' ) { icon = { src: blockDefault, }; } - const renderedIcon = ; + const renderedIcon = ( + + ); const style = showColors ? { backgroundColor: icon && icon.background, diff --git a/packages/block-editor/src/components/off-canvas-editor/block-select-button.js b/packages/block-editor/src/components/off-canvas-editor/block-select-button.js index 9477eb2cda40c0..a3cb64e9298dd4 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block-select-button.js +++ b/packages/block-editor/src/components/off-canvas-editor/block-select-button.js @@ -79,7 +79,11 @@ function ListViewBlockSelectButton( aria-hidden={ true } > - + { + if ( context === 'list-view' ) { + return update; + } + + return pages; + }, example: {}, edit, }; From 2437a110c99c6e6e08794600300064c282004d67 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 14 Dec 2022 17:46:08 +0000 Subject: [PATCH 47/73] Update Changelog for 14.8.0-rc.1 --- changelog.txt | 283 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) diff --git a/changelog.txt b/changelog.txt index 2d52f25c1bf8a6..8774c72580adff 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,288 @@ == Changelog == += 14.8.0-rc.1 = + +## Changelog + +### Enhancements + +#### Block Library +- Add a current-menu-ancestor class to navigation items. ([40778](https://github.com/WordPress/gutenberg/pull/40778)) +- Page List Block: Adds a longdash tree to the parent selector. ([46336](https://github.com/WordPress/gutenberg/pull/46336)) +- Page List Block: Hide page list edit button if no pages are available. ([46331](https://github.com/WordPress/gutenberg/pull/46331)) +- Reusable block: Pluralize the message "Convert to regular blocks" depending on the number of blocks contained. ([45819](https://github.com/WordPress/gutenberg/pull/45819)) +- Add page list to Link UI transforms in Nav block. ([46426](https://github.com/WordPress/gutenberg/pull/46426)) +- Heading Block: Don't rely on the experimental selector anymore. ([46284](https://github.com/WordPress/gutenberg/pull/46284)) +- Media & Text Block: Create undo history when media width is changed. ([46084](https://github.com/WordPress/gutenberg/pull/46084)) +- Navigation block: Add location->primary to fallback nav creation for classic menus. ([45976](https://github.com/WordPress/gutenberg/pull/45976)) +- Navigation block: Update fallback nav creation to the most recently created menu. ([46286](https://github.com/WordPress/gutenberg/pull/46286)) +- Navigation: Add a 'open list view' button. ([46335](https://github.com/WordPress/gutenberg/pull/46335)) +- Navigation: Removes the header from the navigation list view in the experiment. ([46070](https://github.com/WordPress/gutenberg/pull/46070)) +- Page List: Add convert panel to Inspector Controls when within Nav block. ([46352](https://github.com/WordPress/gutenberg/pull/46352)) +- Page List: Prevent users from adding inner blocks to Page List. ([46269](https://github.com/WordPress/gutenberg/pull/46269)) +- Query: Remove color block supports. ([46147](https://github.com/WordPress/gutenberg/pull/46147)) +- Table block: Make `figcaption` styles consistent between editor and front end. ([46172](https://github.com/WordPress/gutenberg/pull/46172)) +- List/quote: Unwrap inner block when pressing Backspace at start. ([45075](https://github.com/WordPress/gutenberg/pull/45075)) + +#### Inspector Controls +- Sidebar Tabs: Refine the use of inspector tabs and disable filters for Nav blocks. ([46346](https://github.com/WordPress/gutenberg/pull/46346)) +- Sidebar Tabs: Use editor settings to override display. ([46321](https://github.com/WordPress/gutenberg/pull/46321)) +- Summary panel: Try improving spacing and grid. ([46267](https://github.com/WordPress/gutenberg/pull/46267)) + +#### Global Styles +- Add Style Book to Global Styles. ([45960](https://github.com/WordPress/gutenberg/pull/45960)) +- Add block preview component in global styles. ([45719](https://github.com/WordPress/gutenberg/pull/45719)) +- Move border from layout to own menu. ([45995](https://github.com/WordPress/gutenberg/pull/45995)) +- Add a css style to theme.json to allow setting of custom css strings. ([46255](https://github.com/WordPress/gutenberg/pull/46255)) +- Expose before filter hook in useSettings for injecting block settings in the editor. ([45089](https://github.com/WordPress/gutenberg/pull/45089)) +- Global styles: Add custom CSS panel to site editor. ([46141](https://github.com/WordPress/gutenberg/pull/46141)) + +#### Site Editor +- Allow adding new templates and template parts directly from the sidebar. ([46458](https://github.com/WordPress/gutenberg/pull/46458)) +- Synchronize the sidebar state in the URL. ([46433](https://github.com/WordPress/gutenberg/pull/46433)) +- Try template drill down on the shell sidebar (browse mode). ([45100](https://github.com/WordPress/gutenberg/pull/45100)) + +#### Block Editor +- Update the synced block hover styles in Inserter. ([46442](https://github.com/WordPress/gutenberg/pull/46442)) +- Add new selector getLastInsertedBlockClientId. ([46531](https://github.com/WordPress/gutenberg/pull/46531)) +- Block editor: Hide fixed contextual toolbar. ([46298](https://github.com/WordPress/gutenberg/pull/46298)) +- Inserter: Pattern title tooltip. ([46419](https://github.com/WordPress/gutenberg/pull/46419)) +- useNestedSettingsUpdate: Prevent unneeded syncing of falsy templateLock values. ([46357](https://github.com/WordPress/gutenberg/pull/46357)) +- Design: Augmented shadows for modals and popovers. ([46228](https://github.com/WordPress/gutenberg/pull/46228)) + +#### Components +- Tabs: Try a simpler tab focus style, alt. ([46276](https://github.com/WordPress/gutenberg/pull/46276)) +- BaseControl: Add convenience hook to generate id-related props. ([46170](https://github.com/WordPress/gutenberg/pull/46170)) +- Dashicon: Refactor to TypeScript. ([45924](https://github.com/WordPress/gutenberg/pull/45924)) +- Lighten borders to gray-600. ([46252](https://github.com/WordPress/gutenberg/pull/46252)) +- Popover: Check positioning by adding and testing is-positioned class. ([46429](https://github.com/WordPress/gutenberg/pull/46429)) + +### Icons +- Icons: Update the border icon. ([46264](https://github.com/WordPress/gutenberg/pull/46264)) + +#### Testing +- Tests: Fix `toBePositionedPopover` matcher message function. ([46239](https://github.com/WordPress/gutenberg/pull/46239)) + +#### Accessibility +- Reorganize the site editor to introduce Browse Mode. ([44770](https://github.com/WordPress/gutenberg/pull/44770)) + +#### Plugin +- Update the Gutenberg plugin to require at least the WP 6.0 version. ([46102](https://github.com/WordPress/gutenberg/pull/46102)) +- PHP: Backport changes from core theme resolver. ([46250](https://github.com/WordPress/gutenberg/pull/46250)) +- Update: Move gutenberg_register_core_block_patterns from 6.1 to 6.2. ([46249](https://github.com/WordPress/gutenberg/pull/46249)) +- Upgrade React packages to v18. ([45235](https://github.com/WordPress/gutenberg/pull/45235)) + +#### Themes +- Empty Theme: Add the `$schema` property in `theme.json` and rename template directories. ([46300](https://github.com/WordPress/gutenberg/pull/46300)) + +#### Mobile +- Mobile: Disable Unsupported Block Editor Tests (Android). ([46542](https://github.com/WordPress/gutenberg/pull/46542)) +- Mobile: Inserter - Remove `.done()` usage. ([46460](https://github.com/WordPress/gutenberg/pull/46460)) +- Mobile: Update Heading block end-to-end test. ([46220](https://github.com/WordPress/gutenberg/pull/46220)) +- Mobile: Updates packages to not use Git HTTPS URLs. ([46422](https://github.com/WordPress/gutenberg/pull/46422)) + +### Bug Fixes + +#### Block Library +- Fix Nav Submenu block Link UI text control. ([46243](https://github.com/WordPress/gutenberg/pull/46243)) +- Fix auto Nav menu creation due to page list inner blocks. ([46223](https://github.com/WordPress/gutenberg/pull/46223)) +- Handle innerContent too when removing innerBlocks. ([46377](https://github.com/WordPress/gutenberg/pull/46377)) +- Image Block: Ensure drag handle matches cursor position when resizing a center aligned image. ([46497](https://github.com/WordPress/gutenberg/pull/46497)) +- Navigation Block: Add social link singular to list of blocks to be allowed. ([46374](https://github.com/WordPress/gutenberg/pull/46374)) +- Navigation Block: Fixes adding a submenu. ([46364](https://github.com/WordPress/gutenberg/pull/46364)) +- Navigation Block: Prevent circular references in navigation block rendering. ([46387](https://github.com/WordPress/gutenberg/pull/46387)) +- Navigation Block: Recursively remove Navigation block’s from appearing inside Navigation block on front of site. ([46279](https://github.com/WordPress/gutenberg/pull/46279)) +- Navigation link: Use stripHTML. ([46317](https://github.com/WordPress/gutenberg/pull/46317)) +- Page List Block: Fix error loading page list parent options. ([46327](https://github.com/WordPress/gutenberg/pull/46327)) +- Query Loop Block: Add migration of colors to v2 deprecation. ([46522](https://github.com/WordPress/gutenberg/pull/46522)) +- Site Logo: Correctly set the image's natural height and width. ([46214](https://github.com/WordPress/gutenberg/pull/46214)) +- Strip markup from link label data in inspector. ([46171](https://github.com/WordPress/gutenberg/pull/46171)) +- Template Parts: Fix modal search stacking context. ([46421](https://github.com/WordPress/gutenberg/pull/46421)) +- Video: Avoid an error when removal is locked. ([46324](https://github.com/WordPress/gutenberg/pull/46324)) +- Layout child fixed size should not be fixed by default and should always have a value set. ([46139](https://github.com/WordPress/gutenberg/pull/46139)) + +#### Blocks +- Paste handler: Remove styles on inline paste. ([46402](https://github.com/WordPress/gutenberg/pull/46402)) +- Improve performance of gutenberg_render_layout_support_flag. ([46074](https://github.com/WordPress/gutenberg/pull/46074)) + +#### Global Styles +- Allow indirect properties when unfiltered_html is not allowed. ([46388](https://github.com/WordPress/gutenberg/pull/46388)) +- Fix Reset to defaults action by moving fills to be within context provider. ([46486](https://github.com/WordPress/gutenberg/pull/46486)) +- Fix duplication of synced block colors in CSS output. ([46297](https://github.com/WordPress/gutenberg/pull/46297)) +- Make style book label font size 11px. ([46341](https://github.com/WordPress/gutenberg/pull/46341)) +- Style Book: Clear Global Styles navigation history when selecting a block. ([46391](https://github.com/WordPress/gutenberg/pull/46391)) + +#### Block Editor +- Block Editor: Fix content locked patterns. ([46494](https://github.com/WordPress/gutenberg/pull/46494)) +- Block Editor: Fix memoized pattern selector dependant arguments. ([46238](https://github.com/WordPress/gutenberg/pull/46238)) +- Block Editor: Restore draggable chip styles. ([46396](https://github.com/WordPress/gutenberg/pull/46396)) +- Block Editor: Revert deoptimization useNestedSettingsUpdate. ([46350](https://github.com/WordPress/gutenberg/pull/46350)) +- Block Editor: Fix some usages of useSelect that return unstable results. ([46226](https://github.com/WordPress/gutenberg/pull/46226)) +- useInnerBlockTemplateSync: Cancel template sync on innerBlocks change or unmount. ([46307](https://github.com/WordPress/gutenberg/pull/46307)) + +#### Patterns +- Add new pattern categories. ([46144](https://github.com/WordPress/gutenberg/pull/46144)) +- Block Editor: Add initial view mode in `BlockPatternSetup`. ([46399](https://github.com/WordPress/gutenberg/pull/46399)) + +#### Site Editor +- Do not remount iframe. ([46431](https://github.com/WordPress/gutenberg/pull/46431)) +- Fix the top bar 'exit' animation. ([46533](https://github.com/WordPress/gutenberg/pull/46533)) +- Keep edited entity in sync when Editor canvas isn't mounted. ([46524](https://github.com/WordPress/gutenberg/pull/46524)) +- [Site Editor]: Add default white background for themes with no `background color` set. ([46314](https://github.com/WordPress/gutenberg/pull/46314)) + +#### Components +- InputControl: Fix `Flex` wrapper usage. ([46213](https://github.com/WordPress/gutenberg/pull/46213)) +- Modal: Fix unexpected modal closing in IME Composition. ([46453](https://github.com/WordPress/gutenberg/pull/46453)) +- MaybeCategoryPanel: Avoid 403 requests for users with low permissions. ([46349](https://github.com/WordPress/gutenberg/pull/46349)) +- Rich text: Add button to clear unknown format. ([44086](https://github.com/WordPress/gutenberg/pull/44086)) + +#### Document Settings +- Fix template title in `summary` panel and requests for low privileged users. ([46304](https://github.com/WordPress/gutenberg/pull/46304)) +- Permalink: Hide edit field for users without publishing capabilities. ([46361](https://github.com/WordPress/gutenberg/pull/46361)) + +#### Patterns +- Content lock: Make filter hook namespace unique. ([46344](https://github.com/WordPress/gutenberg/pull/46344)) + +#### Layout +- Child Layout controls: Fix help text for height. ([46319](https://github.com/WordPress/gutenberg/pull/46319)) + +#### Widgets Editor +- Shortcuts: Add Ctrl+Y for redo to all editor instances on Windows. ([43392](https://github.com/WordPress/gutenberg/pull/43392)) + +#### Block API +- HTML block: Fix parsing. ([27268](https://github.com/WordPress/gutenberg/pull/27268)) + +#### Mobile +- Social Links mobile test: Wait for URL bottom sheet to appear. ([46308](https://github.com/WordPress/gutenberg/pull/46308)) + +### Performance + +#### Components +- Avoid paint on popover when hovering content. ([46201](https://github.com/WordPress/gutenberg/pull/46201)) +- CircularOption: Avoid paint on circular option hover. ([46197](https://github.com/WordPress/gutenberg/pull/46197)) +- Lodash: Replace `_.isEqual()` with `fastDeepEqual`. ([46200](https://github.com/WordPress/gutenberg/pull/46200)) +- Popover: Avoid paint on popovers when scrolling. ([46187](https://github.com/WordPress/gutenberg/pull/46187)) +- Resizable Box: Avoid paint on resizable-box handles. ([46196](https://github.com/WordPress/gutenberg/pull/46196)) +- ListView: Avoid paint on list view item hover. ([46188](https://github.com/WordPress/gutenberg/pull/46188)) + +#### Code Quality +- Lodash: Refactor `blocks` away from `_.find()`. ([46428](https://github.com/WordPress/gutenberg/pull/46428)) +- Lodash: Refactor `core-data` away from `_.find()`. ([46468](https://github.com/WordPress/gutenberg/pull/46468)) +- Lodash: Refactor `edit-site` away from `_.find()`. ([46539](https://github.com/WordPress/gutenberg/pull/46539)) +- Lodash: Refactor away from `_.orderBy()`. ([45146](https://github.com/WordPress/gutenberg/pull/45146)) +- Lodash: Refactor block library away from `_.find()`. ([46430](https://github.com/WordPress/gutenberg/pull/46430)) +- Remove usage of get_default_block_editor_settings. ([46112](https://github.com/WordPress/gutenberg/pull/46112)) + +#### Post Editor +- Lodash: Refactor editor away from `_.find()`. ([46464](https://github.com/WordPress/gutenberg/pull/46464)) +- Lodash: Refactor post editor away from `_.find()`. ([46432](https://github.com/WordPress/gutenberg/pull/46432)) + +#### Block Editor +- Avoid paint on inserter animation. ([46185](https://github.com/WordPress/gutenberg/pull/46185)) +- Improve inserter search performance. ([46153](https://github.com/WordPress/gutenberg/pull/46153)) +- Block Editor: Refactor the "order" state in the block editor reducer to use a map instead of a plain object. ([46221](https://github.com/WordPress/gutenberg/pull/46221)) +- Block Editor: Refactor the block-editor parents state to use maps instead of objects. ([46225](https://github.com/WordPress/gutenberg/pull/46225)) +- Refactor the block-editor "tree" state to use maps instead of objects. ([46229](https://github.com/WordPress/gutenberg/pull/46229)) +- Refactor the block-editor byClientId redux state to use maps instead of plain objects. ([46204](https://github.com/WordPress/gutenberg/pull/46204)) +- Fix typing performance issue for container blocks. ([46527](https://github.com/WordPress/gutenberg/pull/46527)) + +#### Testing +- E2E: Fix performance tests by making inserter search container waiting optional. ([46268](https://github.com/WordPress/gutenberg/pull/46268)) + +#### Mobile +- Columns mobile block: Avoid returning unstable innerWidths from useSelect. ([46403](https://github.com/WordPress/gutenberg/pull/46403)) + +### Experiments + +#### Block Library +- Navigation List View: Remove empty cell when there is no edit button. ([46439](https://github.com/WordPress/gutenberg/pull/46439)) + +#### Web Fonts +- WP Webfonts: Avoid duplicated font families if the font family name was defined using fallback values. ([46378](https://github.com/WordPress/gutenberg/pull/46378)) + +### Documentation +- Adds clarifications and clears up inaccuracies. ([46283](https://github.com/WordPress/gutenberg/pull/46283)) +- Adds details of how to find the .zip file. ([46305](https://github.com/WordPress/gutenberg/pull/46305)) +- Doc: Fix description and documentation for link color support. ([46405](https://github.com/WordPress/gutenberg/pull/46405)) +- Docs: Add missing useState import in BorderBoxControl documentation. ([42067](https://github.com/WordPress/gutenberg/pull/42067)) +- Docs: Add missing useState import in color picker docs. ([42069](https://github.com/WordPress/gutenberg/pull/42069)) +- Docs: Add missing useState import in confirm dialog docs. ([42071](https://github.com/WordPress/gutenberg/pull/42071)) +- Docs: Adds reminder to use Node.js v14 in Quick Start. ([46216](https://github.com/WordPress/gutenberg/pull/46216)) +- Docs: Fix missing link to `primitives` package. ([46290](https://github.com/WordPress/gutenberg/pull/46290)) +- Docs: Update reference to IE 11. ([46296](https://github.com/WordPress/gutenberg/pull/46296)) + +### Code Quality +- Block Editor: Fix `no-node-access` violations in `BlockPreview`. ([46409](https://github.com/WordPress/gutenberg/pull/46409)) +- Block Editor: Fix `no-node-access` violations in `BlockSelectionClearer`. ([46408](https://github.com/WordPress/gutenberg/pull/46408)) +- Columns mobile edit: Remove unused updateBlockSettings action bind. ([46455](https://github.com/WordPress/gutenberg/pull/46455)) +- ESLint: Fix warning in `getBlockAttribute` documentation. ([46500](https://github.com/WordPress/gutenberg/pull/46500)) +- List View: Use default parameters instead of defaultProps. ([46266](https://github.com/WordPress/gutenberg/pull/46266)) +- Removed: Remove small APIs marked to be removed in WP 6.2. ([46106](https://github.com/WordPress/gutenberg/pull/46106)) +- Site Editor: Remove invalid CSS. ([46288](https://github.com/WordPress/gutenberg/pull/46288)) + +#### Block Library +- Group Block: Remove placeholder leftovers. ([46423](https://github.com/WordPress/gutenberg/pull/46423)) +- Group: Remove unnecessary 'useCallback'. ([46418](https://github.com/WordPress/gutenberg/pull/46418)) +- Navigation Block: Add tests for Nav block uncontrolled blocks dirty state checking. ([46329](https://github.com/WordPress/gutenberg/pull/46329)) +- Navigation Block: Update attribute test for `are-blocks-dirty.js`. ([46355](https://github.com/WordPress/gutenberg/pull/46355)) +- Page List Block: Move shared "convert" description to constant. ([46368](https://github.com/WordPress/gutenberg/pull/46368)) +- Page List Block: Simplify Page List convert to links function API. ([46365](https://github.com/WordPress/gutenberg/pull/46365)) +- Query: Cleanup variation picker component. ([46424](https://github.com/WordPress/gutenberg/pull/46424)) +- RNMobile: Add an inline comment to clarify usage of 'hard' limit vs. unbounded query. ([46245](https://github.com/WordPress/gutenberg/pull/46245)) +- Shared standard Link UI component between Nav Link and Submenu blocks. ([46370](https://github.com/WordPress/gutenberg/pull/46370)) +- Template Parts: Remove unnecessary 'useCallback'. ([46420](https://github.com/WordPress/gutenberg/pull/46420)) + +#### Components +- AlignmentMatrixControl: Refactor to TypeScript. ([46162](https://github.com/WordPress/gutenberg/pull/46162)) +- Also ignore `no-node-access` for some components. ([46501](https://github.com/WordPress/gutenberg/pull/46501)) +- Fix `no-node-access` violations in `FocalPointPicker` tests. ([46312](https://github.com/WordPress/gutenberg/pull/46312)) +- Fix `no-node-access` violations in `Popover`. ([46311](https://github.com/WordPress/gutenberg/pull/46311)) +- Fix `no-node-access` violations in `Theme`. ([46310](https://github.com/WordPress/gutenberg/pull/46310)) +- Fix `no-node-access` violations in `ToolsPanel` tests. ([46313](https://github.com/WordPress/gutenberg/pull/46313)) +- withFilters: Use 'act' from React Testing Library. ([46237](https://github.com/WordPress/gutenberg/pull/46237)) + +#### Data Layer +- Data: Add ability to subscribe to one store, remove __unstableSubscribeStore. ([45513](https://github.com/WordPress/gutenberg/pull/45513)) +- ESLint: Fix warnings in the data package. ([46499](https://github.com/WordPress/gutenberg/pull/46499)) + +#### Global Styles +- Add "custom-css" as an acceptable value in the documentation for gutenberg_get_global_stylesheet. ([46493](https://github.com/WordPress/gutenberg/pull/46493)) +- PaletteEdit: Add changelog. ([46095](https://github.com/WordPress/gutenberg/pull/46095)) + +#### Block Editor +- Inserter: Update mobile tab navigation styles. ([46186](https://github.com/WordPress/gutenberg/pull/46186)) + +#### Layout +- Clarify inline comment about switching to `safecss_filter_attr`. ([46061](https://github.com/WordPress/gutenberg/pull/46061)) + +### Tools + +#### Build Tooling +- Adds Github Action to validate Gradle Wrapper. ([46247](https://github.com/WordPress/gutenberg/pull/46247)) +- Prevent api-fetch and core-data from being imported in the block editor package. ([46302](https://github.com/WordPress/gutenberg/pull/46302)) +- Serialize the map objects properly in the Redux dev tools. ([46282](https://github.com/WordPress/gutenberg/pull/46282)) + +#### Testing +- E2E: Fix flaky Block Switcher tests. ([46406](https://github.com/WordPress/gutenberg/pull/46406)) +- end-to-end tests: Add width and color test to button block. ([46452](https://github.com/WordPress/gutenberg/pull/46452)) + + +## First time contributors + +The following PRs were merged by first time contributors: + +- @corentin-gautier: Avoid paint on popover when hovering content. ([46201](https://github.com/WordPress/gutenberg/pull/46201)) +- @ingeniumed: Expose before filter hook in useSettings for injecting block settings in the editor. ([45089](https://github.com/WordPress/gutenberg/pull/45089)) +- @janusqa: Reusable block: Pluralize the message "Convert to regular blocks" depending on the number of blocks contained. ([45819](https://github.com/WordPress/gutenberg/pull/45819)) + + +## Contributors + +The following contributors merged PRs in this release: + +@aaronrobertshaw @ajlende @andrewserong @aristath @chad1008 @chintu51 @corentin-gautier @derekblank @draganescu @ellatrix @geriux @getdave @glendaviesnz @hideokamoto @ingeniumed @jameskoster @janusqa @jasmussen @jffng @jorgefilipecosta @jsnajdr @madhusudhand @MaggieCabrera @Mamaduka @matiasbenedetto @mburridge @mikachan @mirka @noisysocks @ntsekouras @oandregal @oguzkocer @ramonjd @scruffian @SiobhyB @spacedmonkey @t-hamano @talldan @tellthemachines @tyxla @WunderBart @youknowriad + + = 14.7.3 = ## Changelog From eadd4b5af5abc800db3f6e8ba6959b38221ecd6d Mon Sep 17 00:00:00 2001 From: Ryan Welcher Date: Wed, 14 Dec 2022 13:58:14 -0500 Subject: [PATCH 48/73] Clarifying the npm package release process for the RC release. (#46555) --- docs/contributors/code/release.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/contributors/code/release.md b/docs/contributors/code/release.md index f463cb319f6657..492813376b2c9d 100644 --- a/docs/contributors/code/release.md +++ b/docs/contributors/code/release.md @@ -34,6 +34,10 @@ To release a release candidate (RC) version of the plugin, enter `rc`. To releas This will trigger a GitHub Actions (GHA) workflow that bumps the plugin version, builds the Gutenberg plugin .zip file, creates a release draft, and attaches the plugin .zip file to it. This part of the process typically takes a little under six minutes. You'll see that workflow appear at the top of the list, right under the blue banner. Once it's finished, it'll change its status icon from a yellow dot to a green checkmark. You can follow along in a more detailed view by clicking on the workflow. +#### Publishing the @wordpress packages to NPM + +As part of the release candidate (RC) process, all of the `@wordpress` packages are published to NPM. You may see messaging after the ["Build Gutenberg Plugin Zip" action](https://github.com/WordPress/gutenberg/actions/workflows/build-plugin-zip.yml) action has created the draft release that the ["Publish npm packages"](https://github.com/WordPress/gutenberg/actions/workflows/publish-npm-packages.yml) action requires someone with appropriate permissions to trigger the action. This is not the case as this process is automated and it will automatically run after the release notes are published. + #### View the release draft As soon as the workflow has finished, you'll find the release draft under [https://github.com/WordPress/gutenberg/releases](https://github.com/WordPress/gutenberg/releases). The draft is pre-populated with changelog entries based on previous release candidates for this version, and any changes that have since been cherry-picked to the release branch. Thus, when releasing the first stable version of a series, make sure to delete any RC version headers (that are only there for your information) and to move the more recent changes to the correct section (see below). From 271f650c05795ab81cc73b2617ba53485ac57de9 Mon Sep 17 00:00:00 2001 From: Marcelo Serpa <81248+fullofcaffeine@users.noreply.github.com> Date: Wed, 14 Dec 2022 16:23:05 -0600 Subject: [PATCH 49/73] [`customize-widgets/utils/widgetToBlock`] Initialize a widget's `raw_content.content` to an empty string if it's `undefined` (#46487) * Initialize a widget's raw_content.content to an empty string if it's undefined * Do not mutate the instance object's raw_instance property * Add comment --- packages/customize-widgets/src/utils.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/customize-widgets/src/utils.js b/packages/customize-widgets/src/utils.js index f2f254b5ae6af6..8cf2d5990423ca 100644 --- a/packages/customize-widgets/src/utils.js +++ b/packages/customize-widgets/src/utils.js @@ -98,10 +98,14 @@ export function widgetToBlock( { id, idBase, number, instance } ) { const { encoded_serialized_instance: encoded, instance_hash_key: hash, - raw_instance: raw, + raw_instance: rawInstance, ...rest } = instance; + // It's unclear why `content` is sometimes `undefined`, but it shouldn't be. + const rawContent = rawInstance.content || ''; + const raw = { ...rawInstance, content: rawContent }; + if ( idBase === 'block' ) { const parsedBlocks = parse( raw.content, { __unstableSkipAutop: true, From b856a3fc6b72b3c48f35608c68b83d0953959f47 Mon Sep 17 00:00:00 2001 From: Artemio Morales Date: Wed, 14 Dec 2022 18:29:02 -0500 Subject: [PATCH 50/73] Add rough animation to navigation and links (#46342) * Add rough animation to navigation and links * Add support for configuring animation in block settings; revise animation implementation * Fix animation direction; do code cleanup * Add framer dependency and disable import restriction * Fix linter errors * Limit scope to fix tests * Add wrapper for animation logic, update useSelect dependencies * Fix dependency declaration that was causing tests to break --- ...-rest-block-editor-settings-controller.php | 6 ++ .../src/components/block-inspector/index.js | 83 +++++++++++++++++-- .../src/navigation-link/index.php | 32 +++++++ .../src/navigation-submenu/index.php | 32 +++++++ .../block-library/src/navigation/index.php | 32 +++++++ 5 files changed, 180 insertions(+), 5 deletions(-) diff --git a/lib/experimental/class-wp-rest-block-editor-settings-controller.php b/lib/experimental/class-wp-rest-block-editor-settings-controller.php index d95fb497d3a0a3..7031a14e89ccac 100644 --- a/lib/experimental/class-wp-rest-block-editor-settings-controller.php +++ b/lib/experimental/class-wp-rest-block-editor-settings-controller.php @@ -174,6 +174,12 @@ public function get_item_schema() { 'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ), ), + '__experimentalBlockInspectorAnimation' => array( + 'description' => __( 'Whether to enable animation when showing and hiding the block inspector.', 'gutenberg' ), + 'type' => 'object', + 'context' => array( 'site-editor' ), + ), + 'alignWide' => array( 'description' => __( 'Enable/Disable Wide/Full Alignments.', 'gutenberg' ), 'type' => 'boolean', diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index d676bf5dac6a5b..2f3b69131a73e6 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -14,9 +14,10 @@ import { __experimentalHStack as HStack, __experimentalVStack as VStack, Button, + __unstableMotion as motion, } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useMemo, useCallback } from '@wordpress/element'; +import { useMemo, useCallback, Fragment } from '@wordpress/element'; /** * Internal dependencies @@ -171,6 +172,22 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => { const availableTabs = useInspectorControlsTabs( blockType?.name ); const showTabs = availableTabs?.length > 1; + const isOffCanvasNavigationEditorEnabled = + window?.__experimentalEnableOffCanvasNavigationEditor === true; + + const blockInspectorAnimationSettings = useSelect( + ( select ) => { + if ( isOffCanvasNavigationEditorEnabled ) { + const globalBlockInspectorAnimationSettings = + select( blockEditorStore ).getSettings() + .__experimentalBlockInspectorAnimation; + return globalBlockInspectorAnimationSettings[ blockType.name ]; + } + return null; + }, + [ selectedBlockClientId, isOffCanvasNavigationEditorEnabled, blockType ] + ); + if ( count > 1 ) { return (
@@ -231,11 +248,67 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => { /> ); } + return ( - + ( + + { children } + + ) } + > + + + + + ); +}; + +const BlockInspectorSingleBlockWrapper = ( { animate, wrapper, children } ) => { + return animate ? wrapper( children ) : children; +}; + +const AnimatedContainer = ( { + blockInspectorAnimationSettings, + selectedBlockClientId, + children, +} ) => { + const animationOrigin = + blockInspectorAnimationSettings && + blockInspectorAnimationSettings.enterDirection === 'leftToRight' + ? -50 + : 50; + + return ( + + { children } + ); }; diff --git a/packages/block-library/src/navigation-link/index.php b/packages/block-library/src/navigation-link/index.php index 8853d49f6e2cf0..36bfbf5d1fc0ae 100644 --- a/packages/block-library/src/navigation-link/index.php +++ b/packages/block-library/src/navigation-link/index.php @@ -376,3 +376,35 @@ function gutenberg_disable_tabs_for_navigation_link_block( $settings ) { } add_filter( 'block_editor_settings_all', 'gutenberg_disable_tabs_for_navigation_link_block' ); + +/** + * Enables animation of the block inspector for the Navigation Link block. + * + * See: + * - https://github.com/WordPress/gutenberg/pull/46342 + * - https://github.com/WordPress/gutenberg/issues/45884 + * + * @param array $settings Default editor settings. + * @return array Filtered editor settings. + */ +function gutenberg_enable_animation_for_navigation_link_inspector( $settings ) { + $current_animation_settings = _wp_array_get( + $settings, + array( '__experimentalBlockInspectorAnimation' ), + array() + ); + + $settings['__experimentalBlockInspectorAnimation'] = array_merge( + $current_animation_settings, + array( + 'core/navigation-link' => + array( + 'enterDirection' => 'rightToLeft', + ), + ) + ); + + return $settings; +} + +add_filter( 'block_editor_settings_all', 'gutenberg_enable_animation_for_navigation_link_inspector' ); diff --git a/packages/block-library/src/navigation-submenu/index.php b/packages/block-library/src/navigation-submenu/index.php index 99c86493d5b31a..c1c8006039f555 100644 --- a/packages/block-library/src/navigation-submenu/index.php +++ b/packages/block-library/src/navigation-submenu/index.php @@ -321,3 +321,35 @@ function gutenberg_disable_tabs_for_navigation_submenu_block( $settings ) { } add_filter( 'block_editor_settings_all', 'gutenberg_disable_tabs_for_navigation_submenu_block' ); + +/** + * Enables animation of the block inspector for the Navigation Submenu block. + * + * See: + * - https://github.com/WordPress/gutenberg/pull/46342 + * - https://github.com/WordPress/gutenberg/issues/45884 + * + * @param array $settings Default editor settings. + * @return array Filtered editor settings. + */ +function gutenberg_enable_animation_for_navigation_submenu_inspector( $settings ) { + $current_animation_settings = _wp_array_get( + $settings, + array( '__experimentalBlockInspectorAnimation' ), + array() + ); + + $settings['__experimentalBlockInspectorAnimation'] = array_merge( + $current_animation_settings, + array( + 'core/navigation-submenu' => + array( + 'enterDirection' => 'rightToLeft', + ), + ) + ); + + return $settings; +} + +add_filter( 'block_editor_settings_all', 'gutenberg_enable_animation_for_navigation_submenu_inspector' ); diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index 030cab4501eed4..971a5f500e7741 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -856,3 +856,35 @@ function block_core_navigation_typographic_presets_backcompatibility( $parsed_bl } add_filter( 'render_block_data', 'block_core_navigation_typographic_presets_backcompatibility' ); + +/** + * Enables animation of the block inspector for the Navigation block. + * + * See: + * - https://github.com/WordPress/gutenberg/pull/46342 + * - https://github.com/WordPress/gutenberg/issues/45884 + * + * @param array $settings Default editor settings. + * @return array Filtered editor settings. + */ +function gutenberg_enable_animation_for_navigation_inspector( $settings ) { + $current_animation_settings = _wp_array_get( + $settings, + array( '__experimentalBlockInspectorAnimation' ), + array() + ); + + $settings['__experimentalBlockInspectorAnimation'] = array_merge( + $current_animation_settings, + array( + 'core/navigation' => + array( + 'enterDirection' => 'leftToRight', + ), + ) + ); + + return $settings; +} + +add_filter( 'block_editor_settings_all', 'gutenberg_enable_animation_for_navigation_inspector' ); From 489d48a119477e44022e9a3939332bdcc6cc94aa Mon Sep 17 00:00:00 2001 From: Andrew Serong <14988353+andrewserong@users.noreply.github.com> Date: Thu, 15 Dec 2022 15:02:03 +1100 Subject: [PATCH 51/73] Global Styles REST API endpoint: check custom CSS is included before attempting to validate (#46561) * Global Styles REST API endpoint: check custom CSS is included before attempting to validate * Fix linting issue --- ...ass-gutenberg-rest-global-styles-controller-6-2.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php b/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php index 9f762dd961d058..684786ef22d762 100644 --- a/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php +++ b/lib/compat/wordpress-6.2/class-gutenberg-rest-global-styles-controller-6-2.php @@ -151,10 +151,12 @@ protected function prepare_item_for_database( $request ) { if ( isset( $request['styles'] ) || isset( $request['settings'] ) ) { $config = array(); if ( isset( $request['styles'] ) ) { - $config['styles'] = $request['styles']; - $validate_custom_css = $this->validate_custom_css( $request['styles']['css'] ); - if ( is_wp_error( $validate_custom_css ) ) { - return $validate_custom_css; + $config['styles'] = $request['styles']; + if ( isset( $request['styles']['css'] ) ) { + $validate_custom_css = $this->validate_custom_css( $request['styles']['css'] ); + if ( is_wp_error( $validate_custom_css ) ) { + return $validate_custom_css; + } } } elseif ( isset( $existing_config['styles'] ) ) { $config['styles'] = $existing_config['styles']; From 1e6538ba9d6f07a4c7a2fc0ef63ac0ebe932df80 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 15 Dec 2022 09:11:05 +0400 Subject: [PATCH 52/73] Template Parts: Add an option to import widgets from the sidebars (#45509) * Template Part: Introduce an option to import widgets * Only display active sidebars with widgets as options * Use the sidebar name as the new template part title * Support legacy widgets * Prefix imported template parts to avoid name collision * Add missing docblock * Update labels * Move settings into the advanced inspector controls panel * Update API response for sidebars and mark them as 'inactive' * A minor design adjustments * Fix the rendering order bug * Transform legacy widgets before importing * Avoid hardcoding names of the blocks with wildcard transformations * Skip 'wp_inactive_widgets' widget area * Use 'HStack' * Allow overriding Legacy Widget block settings during registration * Override only supports settings * Improve spacing * Add the legacy widget to the post editor Co-authored-by: Robert Anderson --- lib/compat/wordpress-6.2/rest-api.php | 15 ++ lib/compat/wordpress-6.2/theme.php | 23 +++ lib/compat/wordpress-6.2/widgets.php | 32 ++++ lib/load.php | 2 + package-lock.json | 2 + .../template-part/edit/advanced-controls.js | 12 ++ .../src/template-part/edit/import-controls.js | 180 ++++++++++++++++++ .../src/template-part/edit/index.js | 1 + .../template-part/edit/utils/transformers.js | 37 ++++ packages/edit-post/package.json | 1 + packages/edit-post/src/index.js | 2 + packages/edit-site/package.json | 1 + packages/edit-site/src/index.js | 2 + packages/widgets/src/index.js | 14 +- 14 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 lib/compat/wordpress-6.2/theme.php create mode 100644 lib/compat/wordpress-6.2/widgets.php create mode 100644 packages/block-library/src/template-part/edit/import-controls.js create mode 100644 packages/block-library/src/template-part/edit/utils/transformers.js diff --git a/lib/compat/wordpress-6.2/rest-api.php b/lib/compat/wordpress-6.2/rest-api.php index 4900a622b6c01b..12f7afda3b4d5d 100644 --- a/lib/compat/wordpress-6.2/rest-api.php +++ b/lib/compat/wordpress-6.2/rest-api.php @@ -101,3 +101,18 @@ function gutenberg_register_global_styles_endpoints() { $editor_settings->register_routes(); } add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' ); + +/** + * Updates REST API response for the sidebars and marks them as 'inactive'. + * + * Note: This can be a part of the `prepare_item_for_response` in `class-wp-rest-sidebars-controller.php`. + * + * @param WP_REST_Response $response The sidebar response object. + * @return WP_REST_Response $response Updated response object. + */ +function gutenberg_modify_rest_sidebars_response( $response ) { + $response->data['status'] = wp_is_block_theme() ? 'inactive' : 'active'; + + return $response; +} +add_filter( 'rest_prepare_sidebar', 'gutenberg_modify_rest_sidebars_response' ); diff --git a/lib/compat/wordpress-6.2/theme.php b/lib/compat/wordpress-6.2/theme.php new file mode 100644 index 00000000000000..79d55206449472 --- /dev/null +++ b/lib/compat/wordpress-6.2/theme.php @@ -0,0 +1,23 @@ +is_block_theme() ) { + set_theme_mod( 'wp_legacy_sidebars', $wp_registered_sidebars ); + } +} +add_action( 'switch_theme', 'gutenberg_set_legacy_sidebars', 10, 2 ); diff --git a/lib/compat/wordpress-6.2/widgets.php b/lib/compat/wordpress-6.2/widgets.php new file mode 100644 index 00000000000000..19591ae64607e3 --- /dev/null +++ b/lib/compat/wordpress-6.2/widgets.php @@ -0,0 +1,32 @@ + setAttributes( { tagName: value } ) } /> + { ! hasInnerBlocks && ( + + ) } ); } diff --git a/packages/block-library/src/template-part/edit/import-controls.js b/packages/block-library/src/template-part/edit/import-controls.js new file mode 100644 index 00000000000000..1512cd936f02cb --- /dev/null +++ b/packages/block-library/src/template-part/edit/import-controls.js @@ -0,0 +1,180 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { useMemo, useState } from '@wordpress/element'; +import { useDispatch, useSelect, useRegistry } from '@wordpress/data'; +import { + Button, + FlexBlock, + FlexItem, + SelectControl, + __experimentalHStack as HStack, + __experimentalSpacer as Spacer, +} from '@wordpress/components'; +import { + switchToBlockType, + getPossibleBlockTransformations, +} from '@wordpress/blocks'; +import { store as coreStore } from '@wordpress/core-data'; +import { store as noticesStore } from '@wordpress/notices'; + +/** + * Internal dependencies + */ +import { useCreateTemplatePartFromBlocks } from './utils/hooks'; +import { transformWidgetToBlock } from './utils/transformers'; + +export function TemplatePartImportControls( { area, setAttributes } ) { + const [ selectedSidebar, setSelectedSidebar ] = useState( '' ); + const [ isBusy, setIsBusy ] = useState( false ); + + const registry = useRegistry(); + const sidebars = useSelect( ( select ) => { + return select( coreStore ).getSidebars( { + per_page: -1, + _fields: 'id,name,description,status,widgets', + } ); + }, [] ); + const { createErrorNotice } = useDispatch( noticesStore ); + + const createFromBlocks = useCreateTemplatePartFromBlocks( + area, + setAttributes + ); + + const options = useMemo( () => { + const sidebarOptions = ( sidebars ?? [] ) + .filter( + ( widgetArea ) => + widgetArea.id !== 'wp_inactive_widgets' && + widgetArea.widgets.length > 0 + ) + .map( ( widgetArea ) => { + return { + value: widgetArea.id, + label: widgetArea.name, + }; + } ); + + if ( ! sidebarOptions.length ) { + return []; + } + + return [ + { value: '', label: __( 'Select widget area' ) }, + ...sidebarOptions, + ]; + }, [ sidebars ] ); + + async function createFromWidgets( event ) { + event.preventDefault(); + + if ( isBusy || ! selectedSidebar ) { + return; + } + + setIsBusy( true ); + + const sidebar = options.find( + ( { value } ) => value === selectedSidebar + ); + const { getWidgets } = registry.resolveSelect( coreStore ); + + // The widgets API always returns a successful response. + const widgets = await getWidgets( { + sidebar: sidebar.value, + _embed: 'about', + } ); + + const skippedWidgets = new Set(); + const blocks = widgets.flatMap( ( widget ) => { + const block = transformWidgetToBlock( widget ); + + if ( block.name !== 'core/legacy-widget' ) { + return block; + } + + const transforms = getPossibleBlockTransformations( [ + block, + ] ).filter( ( item ) => { + // The block without any transformations can't be a wildcard. + if ( ! item.transforms ) { + return true; + } + + const hasWildCardFrom = item.transforms?.from?.find( + ( from ) => from.blocks && from.blocks.includes( '*' ) + ); + const hasWildCardTo = item.transforms?.to?.find( + ( to ) => to.blocks && to.blocks.includes( '*' ) + ); + + return ! hasWildCardFrom && ! hasWildCardTo; + } ); + + // Skip the block if we have no matching transformations. + if ( ! transforms.length ) { + skippedWidgets.add( widget.id_base ); + return []; + } + + // Try transforming the Legacy Widget into a first matching block. + return switchToBlockType( block, transforms[ 0 ].name ); + } ); + + await createFromBlocks( + blocks, + /* translators: %s: name of the widget area */ + sprintf( __( 'Widget area: %s' ), sidebar.label ) + ); + + if ( skippedWidgets.size ) { + createErrorNotice( + sprintf( + /* translators: %s: the list of widgets */ + __( 'Unable to import the following widgets: %s.' ), + Array.from( skippedWidgets ).join( ', ' ) + ), + { + type: 'snackbar', + } + ); + } + + setIsBusy( false ); + } + + return ( + + + + setSelectedSidebar( value ) } + disabled={ ! options.length } + __next36pxDefaultSize + __nextHasNoMarginBottom + /> + + + + + + + ); +} diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index 83b1a8d450b6ca..502bd3d00feef6 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -141,6 +141,7 @@ export default function TemplatePartEdit( { isEntityAvailable={ isEntityAvailable } templatePartId={ templatePartId } defaultWrapper={ areaObject.tagName } + hasInnerBlocks={ innerBlocks.length > 0 } /> { isPlaceholder && ( diff --git a/packages/block-library/src/template-part/edit/utils/transformers.js b/packages/block-library/src/template-part/edit/utils/transformers.js new file mode 100644 index 00000000000000..fdef84d785b909 --- /dev/null +++ b/packages/block-library/src/template-part/edit/utils/transformers.js @@ -0,0 +1,37 @@ +/** + * WordPress dependencies + */ +import { createBlock, parse } from '@wordpress/blocks'; + +/** + * Converts a widget entity record into a block. + * + * @param {Object} widget The widget entity record. + * @return {Object} a block (converted from the entity record). + */ +export function transformWidgetToBlock( widget ) { + if ( widget.id_base === 'block' ) { + const parsedBlocks = parse( widget.instance.raw.content, { + __unstableSkipAutop: true, + } ); + if ( ! parsedBlocks.length ) { + return createBlock( 'core/paragraph', {}, [] ); + } + + return parsedBlocks[ 0 ]; + } + + let attributes; + if ( widget._embedded.about[ 0 ].is_multi ) { + attributes = { + idBase: widget.id_base, + instance: widget.instance, + }; + } else { + attributes = { + id: widget.id, + }; + } + + return createBlock( 'core/legacy-widget', attributes, [] ); +} diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 3414e958344d23..7967b0d63b1d62 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -52,6 +52,7 @@ "@wordpress/url": "file:../url", "@wordpress/viewport": "file:../viewport", "@wordpress/warning": "file:../warning", + "@wordpress/widgets": "file:../widgets", "classnames": "^2.3.1", "lodash": "^4.17.21", "memize": "^1.1.0", diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 5b7c4e01ead7b1..2ee4dcdab17049 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -10,6 +10,7 @@ import { render, unmountComponentAtNode } from '@wordpress/element'; import { dispatch, select } from '@wordpress/data'; import { addFilter } from '@wordpress/hooks'; import { store as preferencesStore } from '@wordpress/preferences'; +import { registerLegacyWidgetBlock } from '@wordpress/widgets'; /** * Internal dependencies @@ -115,6 +116,7 @@ export function initializeEditor( } registerCoreBlocks(); + registerLegacyWidgetBlock( { inserter: false } ); if ( process.env.IS_GUTENBERG_PLUGIN ) { __experimentalRegisterExperimentalCoreBlocks( { enableFSEBlocks: settings.__unstableEnableFullSiteEditingBlocks, diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 6f42f00b2c625e..a89f52e5dddda5 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -54,6 +54,7 @@ "@wordpress/style-engine": "file:../style-engine", "@wordpress/url": "file:../url", "@wordpress/viewport": "file:../viewport", + "@wordpress/widgets": "file:../widgets", "classnames": "^2.3.1", "colord": "^2.9.2", "downloadjs": "^1.4.7", diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 4b619f7aac9a97..d2cbb2f4aa04d2 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -16,6 +16,7 @@ import { store as editorStore } from '@wordpress/editor'; import { store as interfaceStore } from '@wordpress/interface'; import { store as preferencesStore } from '@wordpress/preferences'; import { addFilter } from '@wordpress/hooks'; +import { registerLegacyWidgetBlock } from '@wordpress/widgets'; /** * Internal dependencies @@ -109,6 +110,7 @@ export function initializeEditor( id, settings ) { dispatch( blocksStore ).__experimentalReapplyBlockTypeFilters(); registerCoreBlocks(); + registerLegacyWidgetBlock( { inserter: false } ); if ( process.env.IS_GUTENBERG_PLUGIN ) { __experimentalRegisterExperimentalCoreBlocks( { enableFSEBlocks: true, diff --git a/packages/widgets/src/index.js b/packages/widgets/src/index.js index 6c88d65556d8b0..e55b16ff12b35d 100644 --- a/packages/widgets/src/index.js +++ b/packages/widgets/src/index.js @@ -18,11 +18,21 @@ export * from './utils'; * Note that for the block to be useful, any scripts required by a widget must * be loaded into the page. * + * @param {Object} supports Block support settings. * @see https://developer.wordpress.org/block-editor/how-to-guides/widgets/legacy-widget-block/ */ -export function registerLegacyWidgetBlock() { +export function registerLegacyWidgetBlock( supports = {} ) { const { metadata, settings, name } = legacyWidget; - registerBlockType( { name, ...metadata }, settings ); + registerBlockType( + { name, ...metadata }, + { + ...settings, + supports: { + ...settings.supports, + ...supports, + }, + } + ); } /** From d8d2d5d03132b67a65b7e78da37a949cada93cee Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 15 Dec 2022 10:35:48 +0400 Subject: [PATCH 53/73] Change SpacingSizesControl ARIA from region to group (#46530) * Change SpacingSizesControl ARIA from region to group * Add changelog entry * Use the correct changelog file --- packages/block-editor/CHANGELOG.md | 6 +++++- .../src/components/spacing-sizes-control/index.js | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 6abc73d7073b60..21666d999c1d15 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -8,7 +8,11 @@ ### Enhancements -- `URLInput`: the `renderSuggestions` callback prop now receives `currentInputValue` as a new parameter ([45806](https://github.com/WordPress/gutenberg/pull/45806)). +- `URLInput`: the `renderSuggestions` callback prop now receives `currentInputValue` as a new parameter ([45806](https://github.com/WordPress/gutenberg/pull/45806)). + +### Bug Fix + +- `SpacingSizesControl`: Change ARIA role from `region` to `group` to avoid unwanted ARIA landmark regions ([#46530](https://github.com/WordPress/gutenberg/pull/46530)). ## 10.5.0 (2022-11-16) diff --git a/packages/block-editor/src/components/spacing-sizes-control/index.js b/packages/block-editor/src/components/spacing-sizes-control/index.js index fb4ce2176b759d..4ec1285db52bb9 100644 --- a/packages/block-editor/src/components/spacing-sizes-control/index.js +++ b/packages/block-editor/src/components/spacing-sizes-control/index.js @@ -78,7 +78,6 @@ export default function SpacingSizesControl( { return (
Date: Thu, 15 Dec 2022 14:59:03 +0800 Subject: [PATCH 54/73] Update copy of add submenu item option (#46564) --- packages/block-editor/src/components/off-canvas-editor/block.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/off-canvas-editor/block.js b/packages/block-editor/src/components/off-canvas-editor/block.js index 49ce5788dfafe0..f07d22bcea3fe0 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block.js +++ b/packages/block-editor/src/components/off-canvas-editor/block.js @@ -386,7 +386,7 @@ function ListViewBlock( { onClose(); } } > - { __( 'Add a submenu item' ) } + { __( 'Add submenu item' ) } ) } From 547542478d3e1d8ca4bc3599c1a437ca27094d2a Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 15 Dec 2022 15:08:22 +0800 Subject: [PATCH 55/73] Fix Off Canvas Editor add submenu item option (#46562) * Fix Off Canvas Editor add submenu item option * Fix comment --- .../src/components/off-canvas-editor/block.js | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/packages/block-editor/src/components/off-canvas-editor/block.js b/packages/block-editor/src/components/off-canvas-editor/block.js index f07d22bcea3fe0..4ad64587796bd7 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block.js +++ b/packages/block-editor/src/components/off-canvas-editor/block.js @@ -42,7 +42,7 @@ import useBlockDisplayInformation from '../use-block-display-information'; import { useBlockLock } from '../block-lock'; function ListViewBlock( { - block, + block: { clientId }, isDragged, isSelected, isBranchSelected, @@ -60,7 +60,6 @@ function ListViewBlock( { } ) { const cellRef = useRef( null ); const [ isHovered, setIsHovered ] = useState( false ); - const { clientId, attributes } = block; const { isLocked, isContentLocked } = useBlockLock( clientId ); const forceSelectionContentLock = useSelect( @@ -87,12 +86,12 @@ function ListViewBlock( { ( isSelected && selectedClientIds[ selectedClientIds.length - 1 ] === clientId ); - const { replaceBlock, toggleBlockHighlight } = + const { insertBlock, replaceBlock, toggleBlockHighlight } = useDispatch( blockEditorStore ); const blockInformation = useBlockDisplayInformation( clientId ); - const blockName = useSelect( - ( select ) => select( blockEditorStore ).getBlockName( clientId ), + const block = useSelect( + ( select ) => select( blockEditorStore ).getBlock( clientId ), [ clientId ] ); @@ -100,7 +99,7 @@ function ListViewBlock( { // since that menu is part of the toolbar in the editor canvas. // List View respects this by also hiding the block settings menu. const showBlockActions = hasBlockSupport( - blockName, + block.name, '__experimentalToolbar', true ); @@ -145,7 +144,7 @@ function ListViewBlock( { const { isTreeGridMounted, expand, collapse } = useListViewContext(); - const isEditable = blockName !== 'core/page-list-item'; + const isEditable = block.name !== 'core/page-list-item'; const hasSiblings = siblingBlockCount > 0; const hasRenderedMovers = showBlockMovers && hasSiblings; const moverCellClassName = classnames( @@ -369,20 +368,34 @@ function ListViewBlock( { const newLink = createBlock( 'core/navigation-link' ); - const newSubmenu = createBlock( - 'core/navigation-submenu', - attributes, - block.innerBlocks - ? [ - ...block.innerBlocks, - newLink, - ] - : [ newLink ] - ); - replaceBlock( - clientId, - newSubmenu - ); + if ( + block.name === + 'core/navigation-submenu' + ) { + const updateSelectionOnInsert = false; + insertBlock( + newLink, + block.innerBlocks.length, + clientId, + updateSelectionOnInsert + ); + } else { + // Convert to a submenu if the block currently isn't one. + const newSubmenu = createBlock( + 'core/navigation-submenu', + block.attributes, + block.innerBlocks + ? [ + ...block.innerBlocks, + newLink, + ] + : [ newLink ] + ); + replaceBlock( + clientId, + newSubmenu + ); + } onClose(); } } > From 3331ee1de8147d613ee45fdb1d20d303c9285b6b Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 15 Dec 2022 15:20:35 +0800 Subject: [PATCH 56/73] Try controlled page list block (#46416) --- packages/block-library/src/page-list/edit.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js index b3d2db31d5e04a..7372191723f466 100644 --- a/packages/block-library/src/page-list/edit.js +++ b/packages/block-library/src/page-list/edit.js @@ -25,7 +25,7 @@ import { Button, } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; -import { useMemo, useState, useEffect } from '@wordpress/element'; +import { useMemo, useState } from '@wordpress/element'; import { useEntityRecords } from '@wordpress/core-data'; import { useSelect, useDispatch } from '@wordpress/data'; @@ -39,6 +39,7 @@ import { convertDescription } from './constants'; // We only show the edit option when page count is <= MAX_PAGE_COUNT // Performance of Navigation Links is not good past this value. const MAX_PAGE_COUNT = 100; +const NOOP = () => {}; export default function PageListEdit( { context, @@ -49,8 +50,6 @@ export default function PageListEdit( { const { parentPageID } = attributes; const [ pages ] = useGetPages(); const { pagesByParentId, totalPages, hasResolvedPages } = usePageData(); - const { replaceInnerBlocks, __unstableMarkNextChangeAsNotPersistent } = - useDispatch( blockEditorStore ); const isNavigationChild = 'showSubmenuIcon' in context; const allowConvertToLinks = @@ -133,6 +132,9 @@ export default function PageListEdit( { renderAppender: false, __unstableDisableDropZone: true, templateLock: 'all', + onInput: NOOP, + onChange: NOOP, + value: blockList, } ); const getBlockContent = () => { @@ -185,13 +187,6 @@ export default function PageListEdit( { } }; - useEffect( () => { - __unstableMarkNextChangeAsNotPersistent(); - if ( blockList ) { - replaceInnerBlocks( clientId, blockList ); - } - }, [ clientId, blockList ] ); - const { replaceBlock, selectBlock } = useDispatch( blockEditorStore ); const { parentNavBlockClientId } = useSelect( ( select ) => { From 9615ed0288e9d32c39f515396b7dd6df337d37a5 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 15 Dec 2022 15:36:25 +0800 Subject: [PATCH 57/73] Style Engine: Add support for text columns (column-count) (#46566) --- packages/style-engine/class-wp-style-engine.php | 6 ++++++ .../style-engine/src/styles/typography/index.ts | 13 +++++++++++++ packages/style-engine/src/test/index.js | 9 ++++++++- packages/style-engine/src/types.ts | 1 + phpunit/style-engine/style-engine-test.php | 4 +++- 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php index f5636c150c3eeb..0fd0aeac60e7c4 100644 --- a/packages/style-engine/class-wp-style-engine.php +++ b/packages/style-engine/class-wp-style-engine.php @@ -206,6 +206,12 @@ final class WP_Style_Engine { ), 'path' => array( 'typography', 'lineHeight' ), ), + 'textColumns' => array( + 'property_keys' => array( + 'default' => 'column-count', + ), + 'path' => array( 'typography', 'textColumns' ), + ), 'textDecoration' => array( 'property_keys' => array( 'default' => 'text-decoration', diff --git a/packages/style-engine/src/styles/typography/index.ts b/packages/style-engine/src/styles/typography/index.ts index 48effa0b2b8a95..75e02c96430512 100644 --- a/packages/style-engine/src/styles/typography/index.ts +++ b/packages/style-engine/src/styles/typography/index.ts @@ -76,6 +76,18 @@ const lineHeight = { }, }; +const textColumns = { + name: 'textColumns', + generate: ( style: Style, options: StyleOptions ) => { + return generateRule( + style, + options, + [ 'typography', 'textColumns' ], + 'columnCount' + ); + }, +}; + const textDecoration = { name: 'textDecoration', generate: ( style: Style, options: StyleOptions ) => { @@ -107,6 +119,7 @@ export default [ fontWeight, letterSpacing, lineHeight, + textColumns, textDecoration, textTransform, ]; diff --git a/packages/style-engine/src/test/index.js b/packages/style-engine/src/test/index.js index a94bd8a4256810..fac55b4000e58f 100644 --- a/packages/style-engine/src/test/index.js +++ b/packages/style-engine/src/test/index.js @@ -72,6 +72,7 @@ describe( 'generate', () => { fontWeight: '800', fontFamily: "'Helvetica Neue',sans-serif", lineHeight: '3.3', + textColumns: '2', textDecoration: 'line-through', letterSpacing: '12px', textTransform: 'uppercase', @@ -88,7 +89,7 @@ describe( 'generate', () => { } ) ).toEqual( - ".some-selector { color: #cccccc; background: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(33,32,33) 42%,rgb(65,88,208) 100%); background-color: #111111; min-height: 50vh; outline-color: red; outline-style: dashed; outline-offset: 2px; outline-width: 4px; margin-top: 11px; margin-right: 12px; margin-bottom: 13px; margin-left: 14px; padding-top: 10px; padding-bottom: 5px; font-family: 'Helvetica Neue',sans-serif; font-size: 2.2rem; font-style: italic; font-weight: 800; letter-spacing: 12px; line-height: 3.3; text-decoration: line-through; text-transform: uppercase; }" + ".some-selector { color: #cccccc; background: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(33,32,33) 42%,rgb(65,88,208) 100%); background-color: #111111; min-height: 50vh; outline-color: red; outline-style: dashed; outline-offset: 2px; outline-width: 4px; margin-top: 11px; margin-right: 12px; margin-bottom: 13px; margin-left: 14px; padding-top: 10px; padding-bottom: 5px; font-family: 'Helvetica Neue',sans-serif; font-size: 2.2rem; font-style: italic; font-weight: 800; letter-spacing: 12px; line-height: 3.3; column-count: 2; text-decoration: line-through; text-transform: uppercase; }" ); } ); @@ -242,6 +243,7 @@ describe( 'getCSSRules', () => { fontWeight: '800', fontFamily: "'Helvetica Neue',sans-serif", lineHeight: '3.3', + textColumns: '2', textDecoration: 'line-through', letterSpacing: '12px', textTransform: 'uppercase', @@ -349,6 +351,11 @@ describe( 'getCSSRules', () => { key: 'lineHeight', value: '3.3', }, + { + selector: '.some-selector', + key: 'columnCount', + value: '2', + }, { selector: '.some-selector', key: 'textDecoration', diff --git a/packages/style-engine/src/types.ts b/packages/style-engine/src/types.ts index 0063cd56621c6c..23d3e38cc43c22 100644 --- a/packages/style-engine/src/types.ts +++ b/packages/style-engine/src/types.ts @@ -52,6 +52,7 @@ export interface Style { fontStyle?: CSSProperties[ 'fontStyle' ]; letterSpacing?: CSSProperties[ 'letterSpacing' ]; lineHeight?: CSSProperties[ 'lineHeight' ]; + textColumns?: CSSProperties[ 'columnCount' ]; textDecoration?: CSSProperties[ 'textDecoration' ]; textTransform?: CSSProperties[ 'textTransform' ]; }; diff --git a/phpunit/style-engine/style-engine-test.php b/phpunit/style-engine/style-engine-test.php index 66d8dd62865272..0588f10ef4fce7 100644 --- a/phpunit/style-engine/style-engine-test.php +++ b/phpunit/style-engine/style-engine-test.php @@ -188,6 +188,7 @@ public function data_wp_style_engine_get_styles() { 'fontStyle' => 'italic', 'fontWeight' => '800', 'lineHeight' => '1.3', + 'textColumns' => '2', 'textDecoration' => 'underline', 'textTransform' => 'uppercase', 'letterSpacing' => '2', @@ -195,13 +196,14 @@ public function data_wp_style_engine_get_styles() { ), 'options' => null, 'expected_output' => array( - 'css' => 'font-size:clamp(2em, 2vw, 4em);font-family:Roboto,Oxygen-Sans,Ubuntu,sans-serif;font-style:italic;font-weight:800;line-height:1.3;text-decoration:underline;text-transform:uppercase;letter-spacing:2;', + 'css' => 'font-size:clamp(2em, 2vw, 4em);font-family:Roboto,Oxygen-Sans,Ubuntu,sans-serif;font-style:italic;font-weight:800;line-height:1.3;column-count:2;text-decoration:underline;text-transform:uppercase;letter-spacing:2;', 'declarations' => array( 'font-size' => 'clamp(2em, 2vw, 4em)', 'font-family' => 'Roboto,Oxygen-Sans,Ubuntu,sans-serif', 'font-style' => 'italic', 'font-weight' => '800', 'line-height' => '1.3', + 'column-count' => '2', 'text-decoration' => 'underline', 'text-transform' => 'uppercase', 'letter-spacing' => '2', From a58c37077e5f24ab5c55f750517266c92dbd22f0 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Thu, 15 Dec 2022 09:55:35 +0200 Subject: [PATCH 58/73] Paste: reuse file transforms for file pasting (#45891) * Paste: reuse file transforms for file pasting * Make sure files is defined * paste multiple file types * fix for gallery block * Make sure gallery block settings sync Co-authored-by: ntsekouras --- .../src/components/copy-handler/index.js | 60 ++++++++++++++++--- .../rich-text/file-paste-handler.js | 13 ---- .../components/rich-text/use-paste-handler.js | 47 ++++++++++----- packages/block-editor/src/utils/pasting.js | 12 +--- packages/block-library/src/gallery/edit.js | 18 +++++- packages/block-library/src/gallery/gallery.js | 16 +---- 6 files changed, 108 insertions(+), 58 deletions(-) delete mode 100644 packages/block-editor/src/components/rich-text/file-paste-handler.js diff --git a/packages/block-editor/src/components/copy-handler/index.js b/packages/block-editor/src/components/copy-handler/index.js index 3e5a50a8fe49ea..3881ff06f2bf30 100644 --- a/packages/block-editor/src/components/copy-handler/index.js +++ b/packages/block-editor/src/components/copy-handler/index.js @@ -7,6 +7,8 @@ import { pasteHandler, store as blocksStore, createBlock, + findTransform, + getBlockTransforms, } from '@wordpress/blocks'; import { documentHasSelection, @@ -84,6 +86,7 @@ export function useClipboardHandler() { __unstableIsSelectionCollapsed, __unstableIsSelectionMergeable, __unstableGetSelectedBlocksWithPartialSelection, + canInsertBlockType, } = useSelect( blockEditorStore ); const { flashBlock, @@ -91,6 +94,7 @@ export function useClipboardHandler() { replaceBlocks, __unstableDeleteSelection, __unstableExpandSelection, + insertBlocks, } = useDispatch( blockEditorStore ); const notifyCopy = useNotifyCopy(); @@ -201,13 +205,55 @@ export function useClipboardHandler() { __experimentalCanUserUseUnfilteredHTML: canUserUseUnfilteredHTML, } = getSettings(); - const { plainText, html } = getPasteEventData( event ); - const blocks = pasteHandler( { - HTML: html, - plainText, - mode: 'BLOCKS', - canUserUseUnfilteredHTML, - } ); + const { plainText, html, files } = getPasteEventData( event ); + let blocks = []; + + if ( files.length ) { + const fromTransforms = getBlockTransforms( 'from' ); + blocks = files + .reduce( ( accumulator, file ) => { + const transformation = findTransform( + fromTransforms, + ( transform ) => + transform.type === 'files' && + transform.isMatch( [ file ] ) + ); + if ( transformation ) { + accumulator.push( + transformation.transform( [ file ] ) + ); + } + return accumulator; + }, [] ) + .flat(); + } else { + blocks = pasteHandler( { + HTML: html, + plainText, + mode: 'BLOCKS', + canUserUseUnfilteredHTML, + } ); + } + + if ( selectedBlockClientIds.length === 1 ) { + const [ selectedBlockClientId ] = selectedBlockClientIds; + + if ( + blocks.every( ( block ) => + canInsertBlockType( + block.name, + selectedBlockClientId + ) + ) + ) { + insertBlocks( + blocks, + undefined, + selectedBlockClientId + ); + return; + } + } replaceBlocks( selectedBlockClientIds, diff --git a/packages/block-editor/src/components/rich-text/file-paste-handler.js b/packages/block-editor/src/components/rich-text/file-paste-handler.js deleted file mode 100644 index 2aae5984389e68..00000000000000 --- a/packages/block-editor/src/components/rich-text/file-paste-handler.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * WordPress dependencies - */ -import { createBlobURL } from '@wordpress/blob'; - -export function filePasteHandler( files ) { - return files - .filter( ( { type } ) => - /^image\/(?:jpe?g|png|gif|webp)$/.test( type ) - ) - .map( ( file ) => `` ) - .join( '' ); -} diff --git a/packages/block-editor/src/components/rich-text/use-paste-handler.js b/packages/block-editor/src/components/rich-text/use-paste-handler.js index 5e4f4260f5001f..67c932aceddcc1 100644 --- a/packages/block-editor/src/components/rich-text/use-paste-handler.js +++ b/packages/block-editor/src/components/rich-text/use-paste-handler.js @@ -4,7 +4,11 @@ import { useRef } from '@wordpress/element'; import { useRefEffect } from '@wordpress/compose'; import { getFilesFromDataTransfer } from '@wordpress/dom'; -import { pasteHandler } from '@wordpress/blocks'; +import { + pasteHandler, + findTransform, + getBlockTransforms, +} from '@wordpress/blocks'; import { isEmpty, insert, @@ -17,7 +21,6 @@ import { isURL } from '@wordpress/url'; /** * Internal dependencies */ -import { filePasteHandler } from './file-paste-handler'; import { addActiveFormats, isShortcode } from './utils'; import { splitValue } from './split-value'; import { shouldDismissPastedFiles } from '../../utils/pasting'; @@ -155,6 +158,12 @@ export function usePasteHandler( props ) { return; } + if ( files?.length ) { + // Allows us to ask for this information when we get a report. + // eslint-disable-next-line no-console + window.console.log( 'Received items:\n\n', files ); + } + // Process any attached files, unless we infer that the files in // question are redundant "screenshots" of the actual HTML payload, // as created by certain office-type programs. @@ -164,23 +173,33 @@ export function usePasteHandler( props ) { files?.length && ! shouldDismissPastedFiles( files, html, plainText ) ) { - const content = pasteHandler( { - HTML: filePasteHandler( files ), - mode: 'BLOCKS', - tagName, - preserveWhiteSpace, - } ); - - // Allows us to ask for this information when we get a report. - // eslint-disable-next-line no-console - window.console.log( 'Received items:\n\n', files ); + const fromTransforms = getBlockTransforms( 'from' ); + const blocks = files + .reduce( ( accumulator, file ) => { + const transformation = findTransform( + fromTransforms, + ( transform ) => + transform.type === 'files' && + transform.isMatch( [ file ] ) + ); + if ( transformation ) { + accumulator.push( + transformation.transform( [ file ] ) + ); + } + return accumulator; + }, [] ) + .flat(); + if ( ! blocks.length ) { + return; + } if ( onReplace && isEmpty( value ) ) { - onReplace( content ); + onReplace( blocks ); } else { splitValue( { value, - pastedBlocks: content, + pastedBlocks: blocks, onReplace, onSplit, onSplitMiddle, diff --git a/packages/block-editor/src/utils/pasting.js b/packages/block-editor/src/utils/pasting.js index 366b79a3294229..e962e11050a1d9 100644 --- a/packages/block-editor/src/utils/pasting.js +++ b/packages/block-editor/src/utils/pasting.js @@ -1,7 +1,6 @@ /** * WordPress dependencies */ -import { createBlobURL } from '@wordpress/blob'; import { getFilesFromDataTransfer } from '@wordpress/dom'; export function getPasteEventData( { clipboardData } ) { @@ -25,21 +24,16 @@ export function getPasteEventData( { clipboardData } ) { } } - const files = getFilesFromDataTransfer( clipboardData ).filter( - ( { type } ) => /^image\/(?:jpe?g|png|gif|webp)$/.test( type ) - ); + const files = getFilesFromDataTransfer( clipboardData ); if ( files.length && ! shouldDismissPastedFiles( files, html, plainText ) ) { - html = files - .map( ( file ) => `` ) - .join( '' ); - plainText = ''; + return { files }; } - return { html, plainText }; + return { html, plainText, files: [] }; } /** diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index c8d6731198c7c6..893167e6690f3c 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -20,6 +20,7 @@ import { MediaPlaceholder, InspectorControls, useBlockProps, + useInnerBlocksProps, BlockControls, MediaReplaceFlow, } from '@wordpress/block-editor'; @@ -63,6 +64,7 @@ const linkOptions = [ }, ]; const ALLOWED_MEDIA_TYPES = [ 'image' ]; +const allowedBlocks = [ 'core/image' ]; const PLACEHOLDER_TEXT = Platform.isNative ? __( 'ADD MEDIA' ) @@ -483,8 +485,20 @@ function GalleryEdit( props ) { className: classnames( className, 'has-nested-images' ), } ); + const innerBlocksProps = useInnerBlocksProps( blockProps, { + allowedBlocks, + orientation: 'horizontal', + renderAppender: false, + __experimentalLayout: { type: 'default', alignments: [] }, + } ); + if ( ! hasImages ) { - return { mediaPlaceholder }; + return ( + + { innerBlocksProps.children } + { mediaPlaceholder } + + ); } const hasLinkTo = linkTo && linkTo !== 'none'; @@ -579,7 +593,7 @@ function GalleryEdit( props ) { ? mediaPlaceholder : undefined } - blockProps={ blockProps } + blockProps={ innerBlocksProps } insertBlocksAfter={ insertBlocksAfter } /> diff --git a/packages/block-library/src/gallery/gallery.js b/packages/block-library/src/gallery/gallery.js index e6176cc8a7256c..957a141d51c6b7 100644 --- a/packages/block-library/src/gallery/gallery.js +++ b/packages/block-library/src/gallery/gallery.js @@ -8,7 +8,6 @@ import classnames from 'classnames'; */ import { RichText, - useInnerBlocksProps, __experimentalGetElementClassName, } from '@wordpress/block-editor'; import { VisuallyHidden } from '@wordpress/components'; @@ -16,8 +15,6 @@ import { __ } from '@wordpress/i18n'; import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; import { View } from '@wordpress/primitives'; -const allowedBlocks = [ 'core/image' ]; - export const Gallery = ( props ) => { const { attributes, @@ -31,16 +28,9 @@ export const Gallery = ( props ) => { const { align, columns, caption, imageCrop } = attributes; - const { children, ...innerBlocksProps } = useInnerBlocksProps( blockProps, { - allowedBlocks, - orientation: 'horizontal', - renderAppender: false, - __experimentalLayout: { type: 'default', alignments: [] }, - } ); - return (
{ } ) } > - { children } - { isSelected && ! children && ( + { blockProps.children } + { isSelected && ! blockProps.children && ( { mediaPlaceholder } From 897d3b3ab9f9edb57bea57972ea830b483309448 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 15 Dec 2022 10:04:32 +0100 Subject: [PATCH 59/73] Add a performance metric to measure typing within containers (#46529) --- bin/plugin/commands/performance.js | 16 ++++ .../assets/small-post-with-containers.html | 77 ++++++++++++++++ .../e2e-tests/config/performance-reporter.js | 28 ++++-- .../specs/performance/post-editor.test.js | 91 +++++++++++++++---- .../specs/performance/site-editor.test.js | 1 + 5 files changed, 189 insertions(+), 24 deletions(-) create mode 100644 packages/e2e-tests/assets/small-post-with-containers.html diff --git a/bin/plugin/commands/performance.js b/bin/plugin/commands/performance.js index 31a537f373731d..81aa39820c8b5a 100644 --- a/bin/plugin/commands/performance.js +++ b/bin/plugin/commands/performance.js @@ -37,6 +37,7 @@ const config = require( '../config' ); * @property {number[]} firstContentfulPaint Represents the time when the browser first renders any text or media. * @property {number[]} firstBlock Represents the time when Puppeteer first sees a block selector in the DOM. * @property {number[]} type Average type time. + * @property {number[]} typeContainer Average type time within a container. * @property {number[]} focus Average block selection time. * @property {number[]} inserterOpen Average time to open global inserter. * @property {number[]} inserterSearch Average time to search the inserter. @@ -56,6 +57,9 @@ const config = require( '../config' ); * @property {number=} type Average type time. * @property {number=} minType Minimum type time. * @property {number=} maxType Maximum type time. + * @property {number=} typeContainer Average type time within a container. + * @property {number=} minTypeContainer Minimum type time within a container. + * @property {number=} maxTypeContainer Maximum type time within a container. * @property {number=} focus Average block selection time. * @property {number=} minFocus Min block selection time. * @property {number=} maxFocus Max block selection time. @@ -129,6 +133,9 @@ function curateResults( results ) { type: average( results.type ), minType: Math.min( ...results.type ), maxType: Math.max( ...results.type ), + typeContainer: average( results.typeContainer ), + minTypeContainer: Math.min( ...results.typeContainer ), + maxTypeContainer: Math.max( ...results.typeContainer ), focus: average( results.focus ), minFocus: Math.min( ...results.focus ), maxFocus: Math.max( ...results.focus ), @@ -393,6 +400,15 @@ async function runPerformanceTests( branches, options ) { type: rawResults.map( ( r ) => r[ branch ].type ), minType: rawResults.map( ( r ) => r[ branch ].minType ), maxType: rawResults.map( ( r ) => r[ branch ].maxType ), + typeContainer: rawResults.map( + ( r ) => r[ branch ].typeContainer + ), + minTypeContainer: rawResults.map( + ( r ) => r[ branch ].minTypeContainer + ), + maxTypeContainer: rawResults.map( + ( r ) => r[ branch ].maxTypeContainer + ), focus: rawResults.map( ( r ) => r[ branch ].focus ), minFocus: rawResults.map( ( r ) => r[ branch ].minFocus ), maxFocus: rawResults.map( ( r ) => r[ branch ].maxFocus ), diff --git a/packages/e2e-tests/assets/small-post-with-containers.html b/packages/e2e-tests/assets/small-post-with-containers.html new file mode 100644 index 00000000000000..f5d32011602dcd --- /dev/null +++ b/packages/e2e-tests/assets/small-post-with-containers.html @@ -0,0 +1,77 @@ + +
+
+

Heading

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+ + + +
    +
  • one
  • + + + +
  • two
  • + + + +
  • three
  • +
+
+ + + +
+

Heading

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+ + + +
    +
  • one
  • + + + +
  • two
  • + + + +
  • three
  • +
+
+ + + +
+

Heading

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+ + + +
    +
  • one
  • + + + +
  • two
  • + + + +
  • three
  • +
+
+
+ + + +

+ \ No newline at end of file diff --git a/packages/e2e-tests/config/performance-reporter.js b/packages/e2e-tests/config/performance-reporter.js index 549855bf297135..e9928008dd6e3b 100644 --- a/packages/e2e-tests/config/performance-reporter.js +++ b/packages/e2e-tests/config/performance-reporter.js @@ -36,6 +36,7 @@ class PerformanceReporter { firstContentfulPaint, firstBlock, type, + typeContainer, focus, listViewOpen, inserterOpen, @@ -68,7 +69,7 @@ Average time to first block: ${ success( if ( type && type.length ) { // eslint-disable-next-line no-console console.log( ` -${ title( 'Typing Performance:' ) } +${ title( 'Typing:' ) } Average time to type character: ${ success( round( average( type ) ) + 'ms' ) } Slowest time to type character: ${ success( round( Math.max( ...type ) ) + 'ms' @@ -78,10 +79,25 @@ Fastest time to type character: ${ success( ) }` ); } + if ( typeContainer && typeContainer.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Typing within a container:' ) } +Average time to type within a container: ${ success( + round( average( typeContainer ) ) + 'ms' + ) } +Slowest time to type within a container: ${ success( + round( Math.max( ...typeContainer ) ) + 'ms' + ) } +Fastest time to type within a container: ${ success( + round( Math.min( ...typeContainer ) ) + 'ms' + ) }` ); + } + if ( focus && focus.length ) { // eslint-disable-next-line no-console console.log( ` -${ title( 'Block Selection Performance:' ) } +${ title( 'Block Selection:' ) } Average time to select a block: ${ success( round( average( focus ) ) + 'ms' ) } Slowest time to select a block: ${ success( round( Math.max( ...focus ) ) + 'ms' @@ -94,7 +110,7 @@ Fastest time to select a block: ${ success( if ( listViewOpen && listViewOpen.length ) { // eslint-disable-next-line no-console console.log( ` -${ title( 'Opening List View Performance:' ) } +${ title( 'Opening List View:' ) } Average time to open list view: ${ success( round( average( listViewOpen ) ) + 'ms' ) } @@ -109,7 +125,7 @@ Fastest time to open list view: ${ success( if ( inserterOpen && inserterOpen.length ) { // eslint-disable-next-line no-console console.log( ` -${ title( 'Opening Global Inserter Performance:' ) } +${ title( 'Opening Global Inserter:' ) } Average time to open global inserter: ${ success( round( average( inserterOpen ) ) + 'ms' ) } @@ -124,7 +140,7 @@ Fastest time to open global inserter: ${ success( if ( inserterSearch && inserterSearch.length ) { // eslint-disable-next-line no-console console.log( ` -${ title( 'Inserter Search Performance:' ) } +${ title( 'Inserter Search:' ) } Average time to type the inserter search input: ${ success( round( average( inserterSearch ) ) + 'ms' ) } @@ -139,7 +155,7 @@ Fastest time to type the inserter search input: ${ success( if ( inserterHover && inserterHover.length ) { // eslint-disable-next-line no-console console.log( ` -${ title( 'Inserter Block Item Hover Performance:' ) } +${ title( 'Inserter Block Item Hover:' ) } Average time to move mouse between two block item in the inserter: ${ success( round( average( inserterHover ) ) + 'ms' ) } diff --git a/packages/e2e-tests/specs/performance/post-editor.test.js b/packages/e2e-tests/specs/performance/post-editor.test.js index 933b4973fb055e..63fa92514728ec 100644 --- a/packages/e2e-tests/specs/performance/post-editor.test.js +++ b/packages/e2e-tests/specs/performance/post-editor.test.js @@ -32,6 +32,23 @@ import { jest.setTimeout( 1000000 ); +async function loadHtmlIntoTheBlockEditor( html ) { + await page.evaluate( ( _html ) => { + const { parse } = window.wp.blocks; + const { dispatch } = window.wp.data; + const blocks = parse( _html ); + + blocks.forEach( ( block ) => { + if ( block.name === 'core/image' ) { + delete block.attributes.id; + delete block.attributes.url; + } + } ); + + dispatch( 'core/block-editor' ).resetBlocks( blocks ); + }, html ); +} + describe( 'Post Editor Performance', () => { const results = { serverResponse: [], @@ -41,6 +58,7 @@ describe( 'Post Editor Performance', () => { firstContentfulPaint: [], firstBlock: [], type: [], + typeContainer: [], focus: [], listViewOpen: [], inserterOpen: [], @@ -51,25 +69,10 @@ describe( 'Post Editor Performance', () => { let traceResults; beforeAll( async () => { - const html = readFile( - join( __dirname, '../../assets/large-post.html' ) - ); - await createNewPost(); - await page.evaluate( ( _html ) => { - const { parse } = window.wp.blocks; - const { dispatch } = window.wp.data; - const blocks = parse( _html ); - - blocks.forEach( ( block ) => { - if ( block.name === 'core/image' ) { - delete block.attributes.id; - delete block.attributes.url; - } - } ); - - dispatch( 'core/block-editor' ).resetBlocks( blocks ); - }, html ); + await loadHtmlIntoTheBlockEditor( + readFile( join( __dirname, '../../assets/large-post.html' ) ) + ); await saveDraft(); } ); @@ -151,6 +154,58 @@ describe( 'Post Editor Performance', () => { } } ); + it( 'Typing within containers', async () => { + // Measuring block selection performance. + await createNewPost(); + await loadHtmlIntoTheBlockEditor( + readFile( + join( + __dirname, + '../../assets/small-post-with-containers.html' + ) + ) + ); + // Select the block where we type in + await page.waitForSelector( 'p[aria-label="Paragraph block"]' ); + await page.click( 'p[aria-label="Paragraph block"]' ); + // Ignore firsted typed character because it's different + // It probably deserves a dedicated metric. + // (isTyping triggers so it's slower) + await page.keyboard.type( 'x' ); + + let i = 10; + await page.tracing.start( { + path: traceFile, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + + while ( i-- ) { + // Wait for the browser to be idle before starting the monitoring. + // eslint-disable-next-line no-restricted-syntax + await page.waitForTimeout( 500 ); + await page.keyboard.type( 'x' ); + } + // eslint-disable-next-line no-restricted-syntax + await page.waitForTimeout( 500 ); + await page.tracing.stop(); + traceResults = JSON.parse( readFile( traceFile ) ); + const [ keyDownEvents, keyPressEvents, keyUpEvents ] = + getTypingEventDurations( traceResults ); + if ( + keyDownEvents.length === keyPressEvents.length && + keyPressEvents.length === keyUpEvents.length + ) { + // The first character typed triggers a longer time (isTyping change) + // It can impact the stability of the metric, so we exclude it. + for ( let j = 1; j < keyDownEvents.length; j++ ) { + results.typeContainer.push( + keyDownEvents[ j ] + keyPressEvents[ j ] + keyUpEvents[ j ] + ); + } + } + } ); + it( 'Selecting blocks', async () => { // Measuring block selection performance. await createNewPost(); diff --git a/packages/e2e-tests/specs/performance/site-editor.test.js b/packages/e2e-tests/specs/performance/site-editor.test.js index eaa3323b7b16c8..e3a2ef86c69328 100644 --- a/packages/e2e-tests/specs/performance/site-editor.test.js +++ b/packages/e2e-tests/specs/performance/site-editor.test.js @@ -51,6 +51,7 @@ describe( 'Site Editor Performance', () => { firstContentfulPaint: [], firstBlock: [], type: [], + typeContainer: [], focus: [], inserterOpen: [], inserterHover: [], From abf426bd83a4ab1469587bd5dabfff6861414fb0 Mon Sep 17 00:00:00 2001 From: Marin Atanasov <8436925+tyxla@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:20:10 +0200 Subject: [PATCH 60/73] Lodash: Refactor components away from `_.find()` (#46537) * Lodash: Refactor components away from _.find() * Add CHANGELOG entry --- packages/components/CHANGELOG.md | 4 +++ packages/components/src/autocomplete/index.js | 4 +-- .../mobile/bottom-sheet/picker-cell.native.js | 7 +--- .../global-styles-context/utils.native.js | 33 ++++++++++--------- packages/components/src/tab-panel/index.tsx | 3 +- 5 files changed, 24 insertions(+), 27 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 7ed9fc6779bbae..f459061f109e19 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -45,6 +45,10 @@ - `Dashicon`: Convert to TypeScript ([#45924](https://github.com/WordPress/gutenberg/pull/45924)). - `PaletteEdit`: add follow up changelog for #45681 and tests [#46095](https://github.com/WordPress/gutenberg/pull/46095). - `AlignmentMatrixControl`: Convert to TypeScript ([#46162](https://github.com/WordPress/gutenberg/pull/46162)). +- `Autocomplete`: Refactor away from `_.find()` ([#46537](https://github.com/WordPress/gutenberg/pull/46537)). +- `TabPanel`: Refactor away from `_.find()` ([#46537](https://github.com/WordPress/gutenberg/pull/46537)). +- `BottomSheetPickerCell`: Refactor away from `_.find()` for mobile ([#46537](https://github.com/WordPress/gutenberg/pull/46537)). +- Refactor global styles context away from `_.find()` for mobile ([#46537](https://github.com/WordPress/gutenberg/pull/46537)). ### Documentation diff --git a/packages/components/src/autocomplete/index.js b/packages/components/src/autocomplete/index.js index 45f5415e55de00..13c27d49b81b15 100644 --- a/packages/components/src/autocomplete/index.js +++ b/packages/components/src/autocomplete/index.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import { find } from 'lodash'; import removeAccents from 'remove-accents'; /** @@ -290,8 +289,7 @@ function useAutocomplete( { const textAfterSelection = getTextContent( slice( record, undefined, getTextContent( record ).length ) ); - const completer = find( - completers, + const completer = completers?.find( ( { triggerPrefix, allowContext } ) => { const index = text.lastIndexOf( triggerPrefix ); diff --git a/packages/components/src/mobile/bottom-sheet/picker-cell.native.js b/packages/components/src/mobile/bottom-sheet/picker-cell.native.js index 42c59cd025adf0..2dbbdd08d4609e 100644 --- a/packages/components/src/mobile/bottom-sheet/picker-cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/picker-cell.native.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { find } from 'lodash'; - /** * Internal dependencies */ @@ -23,7 +18,7 @@ export default function BottomSheetPickerCell( props ) { onChangeValue( newValue ); }; - const option = find( options, { value } ); + const option = options.find( ( opt ) => opt.value === value ); const label = option ? option.label : value; return ( diff --git a/packages/components/src/mobile/global-styles-context/utils.native.js b/packages/components/src/mobile/global-styles-context/utils.native.js index 7d985a9cccb6f6..38536ec3f107f9 100644 --- a/packages/components/src/mobile/global-styles-context/utils.native.js +++ b/packages/components/src/mobile/global-styles-context/utils.native.js @@ -2,7 +2,7 @@ * External dependencies */ import { camelCase } from 'change-case'; -import { find, get } from 'lodash'; +import { get } from 'lodash'; import { Dimensions } from 'react-native'; /** @@ -113,9 +113,9 @@ export function getBlockColors( } if ( ! isCustomColor ) { - const mappedColor = find( defaultColors, { - slug: value, - } ); + const mappedColor = Object.values( defaultColors ?? {} ).find( + ( { slug } ) => slug === value + ); if ( mappedColor ) { blockStyles[ styleKey ] = mappedColor.color; @@ -143,6 +143,7 @@ export function getBlockTypography( const typographyStyles = {}; const customBlockStyles = blockStyleAttributes?.style?.typography || {}; const blockGlobalStyles = baseGlobalStyles?.blocks?.[ blockName ]; + const parsedFontSizes = Object.values( fontSizes ?? {} ); // Global styles. if ( blockGlobalStyles?.typography ) { @@ -153,9 +154,9 @@ export function getBlockTypography( if ( parseInt( fontSize, 10 ) ) { typographyStyles.fontSize = fontSize; } else { - const mappedFontSize = find( fontSizes, { - slug: fontSize, - } ); + const mappedFontSize = parsedFontSizes.find( + ( { slug } ) => slug === fontSize + ); if ( mappedFontSize ) { typographyStyles.fontSize = mappedFontSize?.size; @@ -169,9 +170,9 @@ export function getBlockTypography( } if ( blockStyleAttributes?.fontSize && baseGlobalStyles ) { - const mappedFontSize = find( fontSizes, { - slug: blockStyleAttributes?.fontSize, - } ); + const mappedFontSize = parsedFontSizes.find( + ( { slug } ) => slug === blockStyleAttributes?.fontSize + ); if ( mappedFontSize ) { typographyStyles.fontSize = mappedFontSize?.size; @@ -212,9 +213,9 @@ export function parseStylesVariables( styles, mappedValues, customValues ) { const path = $2.split( '--' ); const mappedPresetValue = mappedValues[ path[ 0 ] ]; if ( mappedPresetValue && mappedPresetValue.slug ) { - const matchedValue = find( mappedPresetValue.values, { - slug: path[ 1 ], - } ); + const matchedValue = Object.values( + mappedPresetValue.values ?? {} + ).find( ( { slug } ) => slug === path[ 1 ] ); return matchedValue?.[ mappedPresetValue.slug ]; } return UNKNOWN_VALUE; @@ -244,9 +245,9 @@ export function parseStylesVariables( styles, mappedValues, customValues ) { if ( variable === 'var' ) { stylesBase = stylesBase.replace( varRegex, ( _$1, $2 ) => { if ( mappedValues?.color ) { - const matchedValue = find( mappedValues.color?.values, { - slug: $2, - } ); + const matchedValue = mappedValues.color?.values?.find( + ( { slug } ) => slug === $2 + ); return `"${ matchedValue?.color }"`; } return UNKNOWN_VALUE; diff --git a/packages/components/src/tab-panel/index.tsx b/packages/components/src/tab-panel/index.tsx index fce688089a55c0..2504f7f214fa62 100644 --- a/packages/components/src/tab-panel/index.tsx +++ b/packages/components/src/tab-panel/index.tsx @@ -2,7 +2,6 @@ * External dependencies */ import classnames from 'classnames'; -import { find } from 'lodash'; /** * WordPress dependencies @@ -102,7 +101,7 @@ export function TabPanel( { ) => { child.click(); }; - const selectedTab = find( tabs, { name: selected } ); + const selectedTab = tabs.find( ( { name } ) => name === selected ); const selectedId = `${ instanceId }-${ selectedTab?.name ?? 'none' }`; useEffect( () => { From 43f2ef4c542faa6be9f80c58cc0628ee4578a579 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Thu, 15 Dec 2022 09:31:25 +0000 Subject: [PATCH 61/73] Fix accessing potentially undefined block editor setting __experimentalBlockInspectorAnimation --- packages/block-editor/src/components/block-inspector/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 2f3b69131a73e6..48d5d982e75089 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -181,7 +181,9 @@ const BlockInspector = ( { showNoBlockSelectedMessage = true } ) => { const globalBlockInspectorAnimationSettings = select( blockEditorStore ).getSettings() .__experimentalBlockInspectorAnimation; - return globalBlockInspectorAnimationSettings[ blockType.name ]; + return globalBlockInspectorAnimationSettings?.[ + blockType.name + ]; } return null; }, From c89dd8bca282386401d458bcfe808d555e143617 Mon Sep 17 00:00:00 2001 From: Ben Dwyer Date: Thu, 15 Dec 2022 10:00:21 +0000 Subject: [PATCH 62/73] Update the update icon (#46575) Co-authored-by: Ben Dwyer --- packages/icons/src/library/update.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/icons/src/library/update.js b/packages/icons/src/library/update.js index d751fb970ea551..09a291c7e97566 100644 --- a/packages/icons/src/library/update.js +++ b/packages/icons/src/library/update.js @@ -4,8 +4,8 @@ import { SVG, Path } from '@wordpress/primitives'; const update = ( - - + + ); From ece606040387b85069419801cb1b99cf851058b2 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Thu, 15 Dec 2022 12:43:42 +0200 Subject: [PATCH 63/73] Block editor: iframe/writing flow: change tab index to 0 (#46323) --- packages/block-editor/src/components/writing-flow/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/writing-flow/index.js b/packages/block-editor/src/components/writing-flow/index.js index b28f065c559c5f..e3100dc3464c65 100644 --- a/packages/block-editor/src/components/writing-flow/index.js +++ b/packages/block-editor/src/components/writing-flow/index.js @@ -44,7 +44,7 @@ export function useWritingFlow() { useArrowNav(), useRefEffect( ( node ) => { - node.tabIndex = -1; + node.tabIndex = 0; node.contentEditable = hasMultiSelection; if ( ! hasMultiSelection ) { From 9c122466100e6e06dd23cca9b6fd744bc97f048a Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 15 Dec 2022 19:55:01 +0800 Subject: [PATCH 64/73] Page List: Add typography supports (#43316) Co-authored-by: Glen Davies --- docs/reference-guides/core-blocks.md | 2 +- packages/block-library/src/page-list/block.json | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index d2d66fc0d12de3..14f800f140aee3 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -428,7 +428,7 @@ Display a list of all pages. ([Source](https://github.com/WordPress/gutenberg/tr - **Name:** core/page-list - **Category:** widgets -- **Supports:** ~~html~~, ~~reusable~~ +- **Supports:** typography (fontSize, lineHeight), ~~html~~, ~~reusable~~ - **Attributes:** parentPageID ## Page List Item diff --git a/packages/block-library/src/page-list/block.json b/packages/block-library/src/page-list/block.json index 2fc6993849d6f7..e8ff316a9fb4f6 100644 --- a/packages/block-library/src/page-list/block.json +++ b/packages/block-library/src/page-list/block.json @@ -30,7 +30,20 @@ ], "supports": { "reusable": false, - "html": false + "html": false, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + } }, "editorStyle": "wp-block-page-list-editor", "style": "wp-block-page-list" From aa9b1d665a10f32182797e0d7e7a559362d1ec1c Mon Sep 17 00:00:00 2001 From: Rich Tabor Date: Thu, 15 Dec 2022 08:25:56 -0500 Subject: [PATCH 65/73] Remove clearable from link ColorPanel item (#46507) --- packages/block-editor/src/hooks/color.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/block-editor/src/hooks/color.js b/packages/block-editor/src/hooks/color.js index 593eea881e3107..fee6f39daa8b20 100644 --- a/packages/block-editor/src/hooks/color.js +++ b/packages/block-editor/src/hooks/color.js @@ -522,8 +522,6 @@ export function ColorEdit( props ) { allSolids, style?.elements?.link?.color?.text ), - clearable: - !! style?.elements?.link?.color?.text, isShownByDefault: defaultColorControls?.link, resetAllFilter: resetAllLinkFilter, }, From 94b4253a0aad603fef897ffdd89df3e19ebba85b Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Thu, 15 Dec 2022 22:42:01 +0900 Subject: [PATCH 66/73] Migrate quote e2e tests to playwright (#46549) * Migrate qupte e2e tests to playwright * Use inline snapshots * Add `deleteAllPosts` * Use accesible selector * Add an inline comment for `pressKeyTimes` --- .../blocks/__snapshots__/quote.test.js.snap | 123 ------- .../specs/editor/blocks/quote.test.js | 199 ----------- test/e2e/specs/editor/blocks/quote.spec.js | 312 +++++++++++++++++- 3 files changed, 300 insertions(+), 334 deletions(-) delete mode 100644 packages/e2e-tests/specs/editor/blocks/__snapshots__/quote.test.js.snap delete mode 100644 packages/e2e-tests/specs/editor/blocks/quote.test.js diff --git a/packages/e2e-tests/specs/editor/blocks/__snapshots__/quote.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/quote.test.js.snap deleted file mode 100644 index 9a3924de6d1dab..00000000000000 --- a/packages/e2e-tests/specs/editor/blocks/__snapshots__/quote.test.js.snap +++ /dev/null @@ -1,123 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Quote can be converted to a pullquote 1`] = ` -" -

one
two

cite
-" -`; - -exports[`Quote can be converted to paragraphs and renders a paragraph for the cite, if it exists 1`] = ` -" -

one

- - - -

two

- - - -

cite

-" -`; - -exports[`Quote can be converted to paragraphs and renders a void paragraph if both the cite and quote are void 1`] = `""`; - -exports[`Quote can be converted to paragraphs and renders one paragraph block per

within quote 1`] = ` -" -

one

- - - -

two

-" -`; - -exports[`Quote can be converted to paragraphs and renders only one paragraph for the cite, if the quote is void 1`] = ` -" -

- - - -

cite

-" -`; - -exports[`Quote can be created by converting a heading 1`] = ` -" -
-

test

-
-" -`; - -exports[`Quote can be created by converting a paragraph 1`] = ` -" -
-

test

-
-" -`; - -exports[`Quote can be created by converting multiple paragraphs 1`] = ` -" -
-

one

- - - -

two

-
-" -`; - -exports[`Quote can be created by typing "/quote" 1`] = ` -" -
-

I’m a quote

-
-" -`; - -exports[`Quote can be created by typing > in front of text of a paragraph block 1`] = ` -" -
-

test

-
-" -`; - -exports[`Quote can be created by using > at the start of a paragraph block 1`] = ` -" -
-

A quote

- - - -

Another paragraph

-
-" -`; - -exports[`Quote can be split at the end 1`] = ` -" -
-

1

-
- - - -

-" -`; - -exports[`Quote can be split at the end 2`] = ` -" -
-

1

- - - -

2

-
-" -`; diff --git a/packages/e2e-tests/specs/editor/blocks/quote.test.js b/packages/e2e-tests/specs/editor/blocks/quote.test.js deleted file mode 100644 index 9b0a4bac546dd3..00000000000000 --- a/packages/e2e-tests/specs/editor/blocks/quote.test.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * WordPress dependencies - */ -import { - clickBlockAppender, - getEditedPostContent, - createNewPost, - pressKeyTimes, - transformBlockTo, - insertBlock, -} from '@wordpress/e2e-test-utils'; - -describe( 'Quote', () => { - beforeEach( async () => { - await createNewPost(); - } ); - - it( 'can be created by using > at the start of a paragraph block', async () => { - // Create a block with some text that will trigger a list creation. - await clickBlockAppender(); - await page.keyboard.type( '> A quote' ); - - // Create a second list item. - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'Another paragraph' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by typing > in front of text of a paragraph block', async () => { - // Create a list with the slash block shortcut. - await clickBlockAppender(); - await page.keyboard.type( 'test' ); - await pressKeyTimes( 'ArrowLeft', 4 ); - await page.keyboard.type( '> ' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by typing "/quote"', async () => { - // Create a list with the slash block shortcut. - await clickBlockAppender(); - await page.keyboard.type( '/quote' ); - await page.waitForXPath( - `//*[contains(@class, "components-autocomplete__result") and contains(@class, "is-selected") and contains(text(), 'Quote')]` - ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'I’m a quote' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by converting a paragraph', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'test' ); - await transformBlockTo( 'Quote' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be created by converting multiple paragraphs', async () => { - await clickBlockAppender(); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await page.keyboard.down( 'Shift' ); - await page.click( '[data-type="core/paragraph"]' ); - await page.keyboard.up( 'Shift' ); - await transformBlockTo( 'Quote' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - describe( 'can be converted to paragraphs', () => { - it( 'and renders one paragraph block per

within quote', async () => { - await insertBlock( 'Quote' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - // Navigate to the citation to select the block. - await page.keyboard.press( 'ArrowRight' ); - await transformBlockTo( 'Unwrap' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'and renders a paragraph for the cite, if it exists', async () => { - await insertBlock( 'Quote' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await page.keyboard.press( 'ArrowRight' ); - await page.keyboard.type( 'cite' ); - await transformBlockTo( 'Unwrap' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'and renders only one paragraph for the cite, if the quote is void', async () => { - await insertBlock( 'Quote' ); - await page.keyboard.press( 'ArrowRight' ); - await page.keyboard.type( 'cite' ); - await transformBlockTo( 'Unwrap' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'and renders a void paragraph if both the cite and quote are void', async () => { - await insertBlock( 'Quote' ); - await page.keyboard.press( 'ArrowRight' ); // Select the quote - await transformBlockTo( 'Unwrap' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - } ); - - it( 'can be created by converting a heading', async () => { - await insertBlock( 'Heading' ); - await page.keyboard.type( 'test' ); - await transformBlockTo( 'Quote' ); - - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be converted to a pullquote', async () => { - await insertBlock( 'Quote' ); - await page.keyboard.type( 'one' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.type( 'two' ); - await page.keyboard.press( 'ArrowRight' ); - await page.keyboard.type( 'cite' ); - await transformBlockTo( 'Pullquote' ); - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be split at the end', async () => { - await insertBlock( 'Quote' ); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Enter' ); - - // Expect empty paragraph outside quote block. - expect( await getEditedPostContent() ).toMatchSnapshot(); - - await page.keyboard.press( 'Backspace' ); - // Allow time for selection to update. - await page.evaluate( () => new Promise( window.requestIdleCallback ) ); - await page.keyboard.type( '2' ); - - // Expect the paragraph to be merged into the quote block. - expect( await getEditedPostContent() ).toMatchSnapshot(); - } ); - - it( 'can be unwrapped on Backspace', async () => { - await insertBlock( 'Quote' ); - - expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` - " -

-

-
- " - ` ); - - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchInlineSnapshot( `""` ); - } ); - - it( 'can be unwrapped with content on Backspace', async () => { - await insertBlock( 'Quote' ); - await page.keyboard.type( '1' ); - await page.keyboard.press( 'ArrowRight' ); - await page.keyboard.type( '2' ); - - expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` - " -
-

1

- 2
- " - ` ); - - await page.keyboard.press( 'ArrowLeft' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'ArrowUp' ); - await page.keyboard.press( 'Backspace' ); - - expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` - " -

1

- - - -
2
- " - ` ); - } ); -} ); diff --git a/test/e2e/specs/editor/blocks/quote.spec.js b/test/e2e/specs/editor/blocks/quote.spec.js index 69d69f0003489f..299d56785d831b 100644 --- a/test/e2e/specs/editor/blocks/quote.spec.js +++ b/test/e2e/specs/editor/blocks/quote.spec.js @@ -3,29 +3,317 @@ */ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); -test.describe( 'adding a quote', () => { +test.describe( 'Quote', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test.afterEach( async ( { requestUtils } ) => { + await requestUtils.deleteAllPosts(); + } ); + test( 'should allow the user to type right away', async ( { - admin, editor, page, } ) => { - await admin.createNewPost(); + await editor.insertBlock( { name: 'core/quote' } ); + // Type content right after. + await page.keyboard.type( 'Quote content' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

Quote content

+
+` + ); + } ); + + test( 'can be created by using > at the start of a paragraph block', async ( { + editor, + page, + } ) => { + // Create a block with some text that will trigger a paragraph creation. + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '> A quote' ); + // Create a second paragraph. + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'Another paragraph' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

A quote

+ + + +

Another paragraph

+
+` + ); + } ); + + test( 'can be created by typing > in front of text of a paragraph block', async ( { + editor, + page, + pageUtils, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'test' ); + await pageUtils.pressKeyTimes( 'ArrowLeft', 'test'.length ); + await page.keyboard.type( '> ' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

test

+
+` + ); + } ); + + test( 'can be created by typing "/quote"', async ( { editor, page } ) => { + // Create a list with the slash block shortcut. + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( '/quote' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'I’m a quote' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

I’m a quote

+
+` + ); + } ); + + test( 'can be created by converting a paragraph', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'test' ); + await editor.transformBlockTo( 'core/quote' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

test

+
+` + ); + } ); + + test( 'can be created by converting multiple paragraphs', async ( { + editor, + page, + } ) => { + await page.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await page.keyboard.down( 'Shift' ); + await page.click( + 'role=document[name="Paragraph block"i] >> text=one' + ); + await page.keyboard.up( 'Shift' ); + await editor.transformBlockTo( 'core/quote' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

one

+ + + +

two

+
+` + ); + } ); - // Inserting a quote block - await editor.insertBlock( { - name: 'core/quote', + test.describe( 'can be converted to paragraphs', () => { + test( 'and renders one paragraph block per

within quote', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + // Navigate to the citation to select the block. + await page.keyboard.press( 'ArrowRight' ); + // Unwrap the block. + await editor.transformBlockTo( '*' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +

one

+ + + +

two

+` + ); } ); - // Type content right after. - await page.keyboard.type( 'Quote content' ); + test( 'and renders a paragraph for the cite, if it exists', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await page.keyboard.press( 'ArrowRight' ); + await page.keyboard.type( 'cite' ); + // Unwrap the block. + await editor.transformBlockTo( '*' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +

one

+ + + +

two

+ + + +

cite

+` + ); + } ); + + test( 'and renders only one paragraph for the cite, if the quote is void', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + await page.keyboard.press( 'ArrowRight' ); + await page.keyboard.type( 'cite' ); + // Unwrap the block. + await editor.transformBlockTo( '*' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +

+ - // Check the content - const content = await editor.getEditedPostContent(); - expect( content ).toBe( + +

cite

+` + ); + } ); + + test( 'and renders a void paragraph if both the cite and quote are void', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + // Select the quote + await page.keyboard.press( 'ArrowRight' ); + // Unwrap the block. + await editor.transformBlockTo( '*' ); + expect( await editor.getEditedPostContent() ).toBe( '' ); + } ); + } ); + + test( 'can be created by converting a heading', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { name: 'core/heading' } ); + await page.keyboard.type( 'test' ); + await editor.transformBlockTo( 'core/quote' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

test

+
+` + ); + } ); + + test( 'can be converted to a pullquote', async ( { editor, page } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + await page.keyboard.type( 'one' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'two' ); + await page.keyboard.press( 'ArrowRight' ); + await page.keyboard.type( 'cite' ); + await editor.transformBlockTo( 'core/pullquote' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +

one
two

cite
+` + ); + } ); + + test( 'can be split at the end', async ( { editor, page } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Enter' ); + // Expect empty paragraph outside quote block. + expect( await editor.getEditedPostContent() ).toBe( `
-

Quote content

+

1

+
+ + + +

+` + ); + await page.keyboard.press( 'Backspace' ); + await page.keyboard.type( '2' ); + // Expect the paragraph to be merged into the quote block. + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

1

+ + + +

2

+
+` + ); + } ); + + test( 'can be unwrapped on Backspace', async ( { editor, page } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

+` + ); + await page.keyboard.press( 'Backspace' ); + expect( await editor.getEditedPostContent() ).toBe( '' ); + } ); + + test( 'can be unwrapped with content on Backspace', async ( { + editor, + page, + pageUtils, + } ) => { + await editor.insertBlock( { name: 'core/quote' } ); + await page.keyboard.type( '1' ); + await page.keyboard.press( 'ArrowRight' ); + await page.keyboard.type( '2' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +
+

1

+2
+` + ); + // Move the cursor to the start of the first paragraph of the quoted block. + await pageUtils.pressKeyTimes( 'ArrowLeft', 4 ); + await page.keyboard.press( 'Backspace' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +

1

+ + + +
2
` ); } ); From c963b315d714e7c962f3f00d9fe43dc96c3c455e Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 15 Dec 2022 15:15:14 +0100 Subject: [PATCH 67/73] Fix region navigation in the site editor (#46525) --- packages/base-styles/_z-index.scss | 5 +- .../higher-order/navigate-regions/style.scss | 52 ++++------------ .../edit-site/src/components/editor/index.js | 17 +----- .../edit-site/src/components/layout/index.js | 59 +++++++++++++------ .../edit-site/src/components/list/style.scss | 9 +-- .../components/interface-skeleton/index.js | 8 ++- .../components/interface-skeleton/style.scss | 14 ----- .../src/components/navigable-region/index.js | 4 +- 8 files changed, 66 insertions(+), 102 deletions(-) diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index ec3be48f1b9752..62a1ab3b19d8f0 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -142,6 +142,9 @@ $z-layers: ( ".skip-to-selected-block": 100000, ".interface-interface-skeleton__actions": 100000, + // The focus styles of the region navigation containers should be above their content. + ".is-focusing-regions {region} :focus::after": 1000000, + // Show NUX tips above popovers, wp-admin menus, submenus, and sidebar: ".nux-dot-tip": 1000001, @@ -177,8 +180,6 @@ $z-layers: ( // Appear under the topbar. ".customize-widgets__block-toolbar": 7, - - ".is-focusing-regions [role='region']:focus .interface-navigable-region__stacker": -1, ); @function z-index( $key ) { diff --git a/packages/components/src/higher-order/navigate-regions/style.scss b/packages/components/src/higher-order/navigate-regions/style.scss index 807dd0e5dcae5f..6e66c854dd70ee 100644 --- a/packages/components/src/higher-order/navigate-regions/style.scss +++ b/packages/components/src/higher-order/navigate-regions/style.scss @@ -4,14 +4,17 @@ } .is-focusing-regions { - [role="region"]:focus { + [role="region"]:focus::after { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + content: ""; + pointer-events: none; outline: 4px solid $components-color-accent; outline-offset: -4px; - - .interface-navigable-region__stacker { - position: relative; - z-index: z-index(".is-focusing-regions [role='region']:focus .interface-navigable-region__stacker"); - } + z-index: z-index(".is-focusing-regions {region} :focus::after"); } // Fixes for edge cases. @@ -25,40 +28,11 @@ // regardles of the CSS used on other components. // Header top bar when Distraction free mode is on. - &.is-distraction-free .interface-interface-skeleton__header { - .interface-navigable-region__stacker, - .edit-post-header { - outline: inherit; - outline-offset: inherit; - } - } - - // Sidebar toggle button shown when navigating regions. - .interface-interface-skeleton__sidebar { - .interface-navigable-region__stacker, - .edit-post-layout__toggle-sidebar-panel { - outline: inherit; - outline-offset: inherit; - } - } - - // Publish sidebar toggle button shown when navigating regions. - .interface-interface-skeleton__actions { - .interface-navigable-region__stacker, - .edit-post-layout__toggle-publish-panel { - outline: inherit; - outline-offset: inherit; - } - } - - // Publish sidebar. - [role="region"].interface-interface-skeleton__actions:focus .editor-post-publish-panel { + &.is-distraction-free .interface-interface-skeleton__header .edit-post-header, + .interface-interface-skeleton__sidebar .edit-post-layout__toggle-sidebar-panel, + .interface-interface-skeleton__actions .edit-post-layout__toggle-publish-panel, + .editor-post-publish-panel { outline: 4px solid $components-color-accent; outline-offset: -4px; } } - -.interface-navigable-region__stacker { - height: 100%; - width: 100%; -} diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 2f2f6264f2e8d8..fe733f49a19590 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -22,7 +22,6 @@ import { EntitiesSavedStates, } from '@wordpress/editor'; import { __ } from '@wordpress/i18n'; -import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies @@ -64,8 +63,6 @@ export default function Editor() { isInserterOpen, isListViewOpen, isSaveViewOpen, - previousShortcut, - nextShortcut, showIconLabels, } = useSelect( ( select ) => { const { @@ -80,9 +77,6 @@ export default function Editor() { } = select( editSiteStore ); const { hasFinishedResolution, getEntityRecord } = select( coreStore ); const { __unstableGetEditorMode } = select( blockEditorStore ); - const { getAllShortcutKeyCombinations } = select( - keyboardShortcutsStore - ); const { getActiveComplementaryArea } = select( interfaceStore ); const postType = getEditedPostType(); const postId = getEditedPostId(); @@ -112,12 +106,6 @@ export default function Editor() { isRightSidebarOpen: getActiveComplementaryArea( editSiteStore.name ), - previousShortcut: getAllShortcutKeyCombinations( - 'core/edit-site/previous-region' - ), - nextShortcut: getAllShortcutKeyCombinations( - 'core/edit-site/next-region' - ), showIconLabels: select( preferencesStore ).get( 'core/edit-site', 'showIconLabels' @@ -178,6 +166,7 @@ export default function Editor() { ) } - shortcuts={ { - previous: previousShortcut, - next: nextShortcut, - } } labels={ { ...interfaceLabels, secondarySidebar: secondarySidebarLabel, diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js index 150f2d9cce882d..e3d2edc217eefd 100644 --- a/packages/edit-site/src/components/layout/index.js +++ b/packages/edit-site/src/components/layout/index.js @@ -12,6 +12,7 @@ import { __experimentalHStack as HStack, __unstableMotion as motion, __unstableAnimatePresence as AnimatePresence, + __unstableUseNavigateRegions as useNavigateRegions, } from '@wordpress/components'; import { useReducedMotion, @@ -22,6 +23,7 @@ import { __ } from '@wordpress/i18n'; import { store as blockEditorStore } from '@wordpress/block-editor'; import { useState, useEffect } from '@wordpress/element'; import { NavigableRegion } from '@wordpress/interface'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies @@ -47,15 +49,28 @@ export default function Layout( { onError } ) { const { params } = useLocation(); const isListPage = getIsListPage( params ); const isEditorPage = ! isListPage; - const { canvasMode, dashboardLink } = useSelect( - ( select ) => ( { - canvasMode: select( editSiteStore ).__unstableGetCanvasMode(), - dashboardLink: - select( editSiteStore ).getSettings() - .__experimentalDashboardLink, - } ), - [] - ); + const { canvasMode, dashboardLink, previousShortcut, nextShortcut } = + useSelect( ( select ) => { + const { getAllShortcutKeyCombinations } = select( + keyboardShortcutsStore + ); + const { __unstableGetCanvasMode, getSettings } = + select( editSiteStore ); + return { + canvasMode: __unstableGetCanvasMode(), + dashboardLink: getSettings().__experimentalDashboardLink, + previousShortcut: getAllShortcutKeyCombinations( + 'core/edit-site/previous-region' + ), + nextShortcut: getAllShortcutKeyCombinations( + 'core/edit-site/next-region' + ), + }; + }, [] ); + const navigateRegionsProps = useNavigateRegions( { + previous: previousShortcut, + next: nextShortcut, + } ); const { __unstableSetCanvasMode } = useDispatch( editSiteStore ); const { clearSelectedBlock } = useDispatch( blockEditorStore ); const disableMotion = useReducedMotion(); @@ -108,13 +123,19 @@ export default function Layout( { onError } ) { <> { fullResizer }
@@ -196,7 +217,8 @@ export default function Layout( { onError } ) { { showSidebar && ( - - + ) } diff --git a/packages/edit-site/src/components/list/style.scss b/packages/edit-site/src/components/list/style.scss index b8c8f2be27db1a..c3359b752fc053 100644 --- a/packages/edit-site/src/components/list/style.scss +++ b/packages/edit-site/src/components/list/style.scss @@ -44,18 +44,11 @@ .interface-interface-skeleton__content { background: $white; padding: $grid-unit-20; - - .interface-navigable-region__stacker { - align-items: center; - } + align-items: center; @include break-medium() { padding: $grid-unit * 9; } - - & > .interface-navigable-region__stacker { - height: auto; - } } } } diff --git a/packages/interface/src/components/interface-skeleton/index.js b/packages/interface/src/components/interface-skeleton/index.js index e8a7a45ede3fa4..fe329a75d43e07 100644 --- a/packages/interface/src/components/interface-skeleton/index.js +++ b/packages/interface/src/components/interface-skeleton/index.js @@ -46,6 +46,7 @@ function InterfaceSkeleton( actions, labels, className, + enableRegionNavigation = true, // Todo: does this need to be a prop. // Can we use a dependency to keyboard-shortcuts directly? shortcuts, @@ -83,8 +84,11 @@ function InterfaceSkeleton( return (
-
- { children } -
+ { children } ); } From da03081e5e5fec8345620070b356c0c8f4c0d682 Mon Sep 17 00:00:00 2001 From: Ella <4710635+ellatrix@users.noreply.github.com> Date: Thu, 15 Dec 2022 16:28:43 +0200 Subject: [PATCH 68/73] DOM: fix findNext/Previous tabbable if target is not in findFocusable list (#46580) --- packages/dom/src/tabbable.js | 37 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/dom/src/tabbable.js b/packages/dom/src/tabbable.js index 66ae1ee7d4fd46..25bc81f2154582 100644 --- a/packages/dom/src/tabbable.js +++ b/packages/dom/src/tabbable.js @@ -159,18 +159,15 @@ export function find( context ) { * @return {Element|undefined} Preceding tabbable element. */ export function findPrevious( element ) { - const focusables = findFocusable( element.ownerDocument.body ); - const index = focusables.indexOf( element ); - - if ( index === -1 ) { - return undefined; - } - - // Remove all focusables after and including `element`. - focusables.length = index; - - const tabbable = filterTabbable( focusables ); - return tabbable[ tabbable.length - 1 ]; + return filterTabbable( findFocusable( element.ownerDocument.body ) ) + .reverse() + .find( ( focusable ) => { + return ( + // eslint-disable-next-line no-bitwise + element.compareDocumentPosition( focusable ) & + element.DOCUMENT_POSITION_PRECEDING + ); + } ); } /** @@ -182,11 +179,13 @@ export function findPrevious( element ) { * @return {Element|undefined} Next tabbable element. */ export function findNext( element ) { - const focusables = findFocusable( element.ownerDocument.body ); - const index = focusables.indexOf( element ); - - // Remove all focusables before and including `element`. - const remaining = focusables.slice( index + 1 ); - - return filterTabbable( remaining )[ 0 ]; + return filterTabbable( findFocusable( element.ownerDocument.body ) ).find( + ( focusable ) => { + return ( + // eslint-disable-next-line no-bitwise + element.compareDocumentPosition( focusable ) & + element.DOCUMENT_POSITION_FOLLOWING + ); + } + ); } From 7973d9cc5890b64c78e86cb86e1d52d8df55fc69 Mon Sep 17 00:00:00 2001 From: Ramon Date: Fri, 16 Dec 2022 10:26:16 +1100 Subject: [PATCH 69/73] Fluid typography: add configurable settings for minimum font size to theme.json (#42489) * Reinstating the commit in edeaca7068196f8960453965fe7fa64632d573ef so we can test fluid configuration Update docs * Adding a filter pre-calculations * $fluid_settings was missing * Removing filter Pulling across config changes to JS * Add config slot for minFontSize * Adding config to test theme * Updating args to include `minimumFontSizeLimit` Fixed `typographySettings` check * Updating tests and fixing settings logic Updating schema Changelogs * Ensure that a theme's fluid font size settings are used when calculating custom font sizes and search block font sizes. * Validating incoming viewport widths and min font size against accepted units. * Ensure we check for fluid settings validity in getTypographyClassesAndStyles * Rolling back configurable values to the global minimum font size only. Updating tests. * Remove config assignment from getTypographyClassesAndStyles and updated tests. * Adding frontend test for unsupported units in min font size config * simplifying condition so that it's readable in screen widths < 100000px --- .../theme-json-reference/theme-json-living.md | 2 +- lib/block-supports/typography.php | 11 ++- packages/block-editor/CHANGELOG.md | 1 + packages/block-editor/README.md | 3 +- .../src/components/font-sizes/fluid-utils.js | 8 +- packages/block-editor/src/hooks/font-size.js | 17 +++- .../src/hooks/test/use-typography-props.js | 26 +++++ .../src/hooks/use-typography-props.js | 22 +++-- packages/block-library/src/search/edit.js | 4 +- packages/edit-site/CHANGELOG.md | 6 +- .../global-styles/test/typography-utils.js | 95 ++++++++++++++----- .../global-styles/typography-utils.js | 28 +++++- phpunit/block-supports/typography-test.php | 49 ++++++---- .../style.css | 8 ++ .../theme.json | 11 +++ schemas/json/theme.json | 19 +++- 16 files changed, 243 insertions(+), 67 deletions(-) create mode 100644 phpunit/data/themedir1/block-theme-child-with-fluid-typography-config/style.css create mode 100644 phpunit/data/themedir1/block-theme-child-with-fluid-typography-config/theme.json diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index a694cf63e909fa..9b074edace6ae5 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -119,7 +119,7 @@ Settings related to typography. | customFontSize | boolean | true | | | fontStyle | boolean | true | | | fontWeight | boolean | true | | -| fluid | boolean | | | +| fluid | undefined | false | | | letterSpacing | boolean | true | | | lineHeight | boolean | false | | | textDecoration | boolean | true | | diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 01d223b84281eb..809fba1a6e7aca 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -451,18 +451,25 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty // Checks if fluid font sizes are activated. $typography_settings = gutenberg_get_global_settings( array( 'typography' ) ); - $should_use_fluid_typography = isset( $typography_settings['fluid'] ) && true === $typography_settings['fluid'] ? true : $should_use_fluid_typography; + $should_use_fluid_typography + = isset( $typography_settings['fluid'] ) && + ( true === $typography_settings['fluid'] || is_array( $typography_settings['fluid'] ) ) ? + true : + $should_use_fluid_typography; if ( ! $should_use_fluid_typography ) { return $preset['size']; } + $fluid_settings = isset( $typography_settings['fluid'] ) && is_array( $typography_settings['fluid'] ) ? $typography_settings['fluid'] : array(); + // Defaults. $default_maximum_viewport_width = '1600px'; $default_minimum_viewport_width = '768px'; $default_minimum_font_size_factor = 0.75; $default_scale_factor = 1; - $default_minimum_font_size_limit = '14px'; + $has_min_font_size = isset( $fluid_settings['minFontSize'] ) && ! empty( gutenberg_get_typography_value_and_unit( $fluid_settings['minFontSize'] ) ); + $default_minimum_font_size_limit = $has_min_font_size ? $fluid_settings['minFontSize'] : '14px'; // Font sizes. $fluid_font_size_settings = isset( $preset['fluid'] ) ? $preset['fluid'] : null; diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 21666d999c1d15..34cd726c55fdf5 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -9,6 +9,7 @@ ### Enhancements - `URLInput`: the `renderSuggestions` callback prop now receives `currentInputValue` as a new parameter ([45806](https://github.com/WordPress/gutenberg/pull/45806)). +- Fluid typography: add configurable fluid typography settings for minimum font size to theme.json ([#42489](https://github.com/WordPress/gutenberg/pull/42489)). ### Bug Fix diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 5760f1584c5db8..5a861eaa1acad7 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -428,6 +428,7 @@ _Parameters_ - _args.minimumFontSize_ `?string`: Minimum font size for any clamp() calculation. Optional. - _args.scaleFactor_ `?number`: A scale factor to determine how fast a font scales within boundaries. Optional. - _args.minimumFontSizeFactor_ `?number`: How much to scale defaultFontSize by to derive minimumFontSize. Optional. +- _args.minimumFontSizeLimit_ `?string`: The smallest a calculated font size may be. Optional. _Returns_ @@ -520,7 +521,7 @@ attributes. _Parameters_ - _attributes_ `Object`: Block attributes. -- _isFluidFontSizeActive_ `boolean`: Whether the function should try to convert font sizes to fluid values. +- _fluidTypographySettings_ `Object|boolean`: If boolean, whether the function should try to convert font sizes to fluid values, otherwise an object containing theme fluid typography settings. _Returns_ diff --git a/packages/block-editor/src/components/font-sizes/fluid-utils.js b/packages/block-editor/src/components/font-sizes/fluid-utils.js index de8a27e3014e88..f5816e9823d7a3 100644 --- a/packages/block-editor/src/components/font-sizes/fluid-utils.js +++ b/packages/block-editor/src/components/font-sizes/fluid-utils.js @@ -40,6 +40,7 @@ const DEFAULT_MINIMUM_FONT_SIZE_LIMIT = '14px'; * @param {?string} args.minimumFontSize Minimum font size for any clamp() calculation. Optional. * @param {?number} args.scaleFactor A scale factor to determine how fast a font scales within boundaries. Optional. * @param {?number} args.minimumFontSizeFactor How much to scale defaultFontSize by to derive minimumFontSize. Optional. + * @param {?string} args.minimumFontSizeLimit The smallest a calculated font size may be. Optional. * * @return {string|null} A font-size value using clamp(). */ @@ -51,8 +52,13 @@ export function getComputedFluidTypographyValue( { maximumViewPortWidth = DEFAULT_MAXIMUM_VIEWPORT_WIDTH, scaleFactor = DEFAULT_SCALE_FACTOR, minimumFontSizeFactor = DEFAULT_MINIMUM_FONT_SIZE_FACTOR, - minimumFontSizeLimit = DEFAULT_MINIMUM_FONT_SIZE_LIMIT, + minimumFontSizeLimit, } ) { + // Validate incoming settings and set defaults. + minimumFontSizeLimit = !! getTypographyValueAndUnit( minimumFontSizeLimit ) + ? minimumFontSizeLimit + : DEFAULT_MINIMUM_FONT_SIZE_LIMIT; + /* * Calculates missing minimumFontSize and maximumFontSize from * defaultFontSize if provided. diff --git a/packages/block-editor/src/hooks/font-size.js b/packages/block-editor/src/hooks/font-size.js index 6cb950afc45641..0c7a71fd23d683 100644 --- a/packages/block-editor/src/hooks/font-size.js +++ b/packages/block-editor/src/hooks/font-size.js @@ -324,13 +324,22 @@ function addEditPropsForFluidCustomFontSizes( blockType ) { // BlockListContext.Provider. If we set fontSize using editor. // BlockListBlock instead of using getEditWrapperProps then the value is // clobbered when the core/style/addEditProps filter runs. - const isFluidTypographyEnabled = - !! select( blockEditorStore ).getSettings().__experimentalFeatures + const fluidTypographyConfig = + select( blockEditorStore ).getSettings().__experimentalFeatures ?.typography?.fluid; + const fluidTypographySettings = + typeof fluidTypographyConfig === 'object' + ? fluidTypographyConfig + : {}; + const newFontSize = - fontSize && isFluidTypographyEnabled - ? getComputedFluidTypographyValue( { fontSize } ) + fontSize && !! fluidTypographyConfig + ? getComputedFluidTypographyValue( { + fontSize, + minimumFontSizeLimit: + fluidTypographySettings?.minFontSize, + } ) : null; if ( newFontSize === null ) { diff --git a/packages/block-editor/src/hooks/test/use-typography-props.js b/packages/block-editor/src/hooks/test/use-typography-props.js index 00557881467ca8..12336eb2c44afd 100644 --- a/packages/block-editor/src/hooks/test/use-typography-props.js +++ b/packages/block-editor/src/hooks/test/use-typography-props.js @@ -47,4 +47,30 @@ describe( 'getTypographyClassesAndStyles', () => { }, } ); } ); + + it( 'should return configured fluid font size styles', () => { + const attributes = { + fontFamily: 'tofu', + style: { + typography: { + textDecoration: 'underline', + fontSize: '2rem', + textTransform: 'uppercase', + }, + }, + }; + expect( + getTypographyClassesAndStyles( attributes, { + minFontSize: '1rem', + } ) + ).toEqual( { + className: 'has-tofu-font-family', + style: { + textDecoration: 'underline', + fontSize: + 'clamp(1.5rem, 1.5rem + ((1vw - 0.48rem) * 0.962), 2rem)', + textTransform: 'uppercase', + }, + } ); + } ); } ); diff --git a/packages/block-editor/src/hooks/use-typography-props.js b/packages/block-editor/src/hooks/use-typography-props.js index d70ae08aafc593..da5869ad9aec07 100644 --- a/packages/block-editor/src/hooks/use-typography-props.js +++ b/packages/block-editor/src/hooks/use-typography-props.js @@ -19,23 +19,31 @@ import { getComputedFluidTypographyValue } from '../components/font-sizes/fluid- * Provides the CSS class names and inline styles for a block's typography support * attributes. * - * @param {Object} attributes Block attributes. - * @param {boolean} isFluidFontSizeActive Whether the function should try to convert font sizes to fluid values. + * @param {Object} attributes Block attributes. + * @param {Object|boolean} fluidTypographySettings If boolean, whether the function should try to convert font sizes to fluid values, + * otherwise an object containing theme fluid typography settings. * * @return {Object} Typography block support derived CSS classes & styles. */ export function getTypographyClassesAndStyles( attributes, - isFluidFontSizeActive + fluidTypographySettings ) { let typographyStyles = attributes?.style?.typography || {}; - if ( isFluidFontSizeActive ) { + if ( + !! fluidTypographySettings && + ( true === fluidTypographySettings || + Object.keys( fluidTypographySettings ).length !== 0 ) + ) { + const newFontSize = + getComputedFluidTypographyValue( { + fontSize: attributes?.style?.typography?.fontSize, + minimumFontSizeLimit: fluidTypographySettings?.minFontSize, + } ) || attributes?.style?.typography?.fontSize; typographyStyles = { ...typographyStyles, - fontSize: getComputedFluidTypographyValue( { - fontSize: attributes?.style?.typography?.fontSize, - } ), + fontSize: newFontSize, }; } diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index a3db69287f8f73..78ff685ff01fe1 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -114,10 +114,10 @@ export default function SearchEdit( { } const colorProps = useColorProps( attributes ); - const fluidTypographyEnabled = useSetting( 'typography.fluid' ); + const fluidTypographySettings = useSetting( 'typography.fluid' ); const typographyProps = useTypographyProps( attributes, - fluidTypographyEnabled + fluidTypographySettings ); const unitControlInstanceId = useInstanceId( UnitControl ); const unitControlInputId = `wp-block-search__width-${ unitControlInstanceId }`; diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md index 83dcb848d66b3e..357668d600d11e 100644 --- a/packages/edit-site/CHANGELOG.md +++ b/packages/edit-site/CHANGELOG.md @@ -4,7 +4,11 @@ ### Breaking Changes -- Updated dependencies to require React 18 ([45235](https://github.com/WordPress/gutenberg/pull/45235)) +- Updated dependencies to require React 18 ([45235](https://github.com/WordPress/gutenberg/pull/45235)). + +### Enhancements + +- Fluid typography: add configurable fluid typography settings for minimum font size to theme.json ([#42489](https://github.com/WordPress/gutenberg/pull/42489)). ## 4.19.0 (2022-11-16) diff --git a/packages/edit-site/src/components/global-styles/test/typography-utils.js b/packages/edit-site/src/components/global-styles/test/typography-utils.js index 647b02cb4be1fb..9d0213cb53e554 100644 --- a/packages/edit-site/src/components/global-styles/test/typography-utils.js +++ b/packages/edit-site/src/components/global-styles/test/typography-utils.js @@ -7,7 +7,8 @@ describe( 'typography utils', () => { describe( 'getTypographyFontSizeValue', () => { [ { - message: 'returns value when fluid typography is deactivated', + message: + 'should return value when fluid typography is not active', preset: { size: '28px', }, @@ -16,7 +17,7 @@ describe( 'typography utils', () => { }, { - message: 'returns value where font size is 0', + message: 'should return value where font size is 0', preset: { size: 0, }, @@ -25,7 +26,7 @@ describe( 'typography utils', () => { }, { - message: "returns value where font size is '0'", + message: "should return value where font size is '0'", preset: { size: '0', }, @@ -34,7 +35,7 @@ describe( 'typography utils', () => { }, { - message: 'returns value where `size` is `null`.', + message: 'should return value where `size` is `null`.', preset: { size: null, }, @@ -43,7 +44,7 @@ describe( 'typography utils', () => { }, { - message: 'returns value when fluid is `false`', + message: 'should return value when fluid is `false`', preset: { size: '28px', fluid: false, @@ -55,7 +56,7 @@ describe( 'typography utils', () => { }, { - message: 'returns already clamped value', + message: 'should return already clamped value', preset: { size: 'clamp(21px, 1.313rem + ((1vw - 7.68px) * 2.524), 42px)', fluid: false, @@ -68,7 +69,7 @@ describe( 'typography utils', () => { }, { - message: 'returns value with unsupported unit', + message: 'should return value with unsupported unit', preset: { size: '1000%', fluid: false, @@ -80,7 +81,7 @@ describe( 'typography utils', () => { }, { - message: 'returns clamp value with rem min and max units', + message: 'should return clamp value with rem min and max units', preset: { size: '1.75rem', }, @@ -92,7 +93,7 @@ describe( 'typography utils', () => { }, { - message: 'returns clamp value with eem min and max units', + message: 'should return clamp value with eem min and max units', preset: { size: '1.75em', }, @@ -104,7 +105,7 @@ describe( 'typography utils', () => { }, { - message: 'returns clamp value for floats', + message: 'should return clamp value for floats', preset: { size: '100.175px', }, @@ -116,7 +117,8 @@ describe( 'typography utils', () => { }, { - message: 'coerces integer to `px` and returns clamp value', + message: + 'should coerce integer to `px` and returns clamp value', preset: { size: 33, fluid: true, @@ -129,7 +131,7 @@ describe( 'typography utils', () => { }, { - message: 'coerces float to `px` and returns clamp value', + message: 'should coerce float to `px` and returns clamp value', preset: { size: 100.23, fluid: true, @@ -142,7 +144,8 @@ describe( 'typography utils', () => { }, { - message: 'returns clamp value when `fluid` is empty array', + message: + 'should return clamp value when `fluid` is empty array', preset: { size: '28px', fluid: [], @@ -155,7 +158,7 @@ describe( 'typography utils', () => { }, { - message: 'returns clamp value when `fluid` is `null`', + message: 'should return clamp value when `fluid` is `null`', preset: { size: '28px', fluid: null, @@ -169,7 +172,7 @@ describe( 'typography utils', () => { { message: - 'returns clamp value if min font size is greater than max', + 'should return clamp value if min font size is greater than max', preset: { size: '3rem', fluid: { @@ -185,7 +188,7 @@ describe( 'typography utils', () => { }, { - message: 'returns value with invalid min/max fluid units', + message: 'should return value with invalid min/max fluid units', preset: { size: '10em', fluid: { @@ -201,7 +204,7 @@ describe( 'typography utils', () => { { message: - 'returns value when size is < lower bounds and no fluid min/max set', + 'should return value when size is < lower bounds and no fluid min/max set', preset: { size: '3px', }, @@ -213,7 +216,7 @@ describe( 'typography utils', () => { { message: - 'returns value when size is equal to lower bounds and no fluid min/max set', + 'should return value when size is equal to lower bounds and no fluid min/max set', preset: { size: '14px', }, @@ -224,7 +227,8 @@ describe( 'typography utils', () => { }, { - message: 'returns clamp value with different min max units', + message: + 'should return clamp value with different min max units', preset: { size: '28px', fluid: { @@ -240,7 +244,8 @@ describe( 'typography utils', () => { }, { - message: 'returns clamp value where no fluid max size is set', + message: + 'should return clamp value where no fluid max size is set', preset: { size: '28px', fluid: { @@ -255,7 +260,8 @@ describe( 'typography utils', () => { }, { - message: 'returns clamp value where no fluid min size is set', + message: + 'should return clamp value where no fluid min size is set', preset: { size: '28px', fluid: { @@ -320,7 +326,7 @@ describe( 'typography utils', () => { { message: - 'returns clamp value when min and max font sizes are equal', + 'should return clamp value when min and max font sizes are equal', preset: { size: '4rem', fluid: { @@ -333,8 +339,51 @@ describe( 'typography utils', () => { }, expected: 'clamp(30px, 1.875rem + ((1vw - 7.68px) * 1), 30px)', }, + + { + message: + 'should use default min font size value where min font size unit in fluid config is not supported', + preset: { + size: '15px', + }, + typographySettings: { + fluid: { + minFontSize: '16%', + }, + }, + expected: + 'clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.12), 15px)', + }, + + // Equivalent custom config PHP unit tests in `test_should_covert_font_sizes_to_fluid_values()`. + { + message: 'should return clamp value using custom fluid config', + preset: { + size: '17px', + }, + typographySettings: { + fluid: { + minFontSize: '16px', + }, + }, + expected: 'clamp(16px, 1rem + ((1vw - 7.68px) * 0.12), 17px)', + }, + + { + message: + 'should return value when font size <= custom min font size bound', + preset: { + size: '15px', + }, + typographySettings: { + fluid: { + minFontSize: '16px', + }, + }, + expected: '15px', + }, ].forEach( ( { message, preset, typographySettings, expected } ) => { - it( `should ${ message }`, () => { + it( `${ message }`, () => { expect( getTypographyFontSizeValue( preset, typographySettings ) ).toBe( expected ); diff --git a/packages/edit-site/src/components/global-styles/typography-utils.js b/packages/edit-site/src/components/global-styles/typography-utils.js index a792d1875005c2..5720fdeb6ce91e 100644 --- a/packages/edit-site/src/components/global-styles/typography-utils.js +++ b/packages/edit-site/src/components/global-styles/typography-utils.js @@ -23,13 +23,23 @@ import { getComputedFluidTypographyValue } from '@wordpress/block-editor'; * @property {boolean|FluidPreset|undefined} fluid A font size slug */ +/** + * @typedef {Object} TypographySettings + * @property {?string|?number} size A default font size. + * @property {?string} minViewPortWidth Minimum viewport size from which type will have fluidity. Optional if size is specified. + * @property {?string} maxViewPortWidth Maximum size up to which type will have fluidity. Optional if size is specified. + * @property {?number} scaleFactor A scale factor to determine how fast a font scales within boundaries. Optional. + * @property {?number} minFontSizeFactor How much to scale defaultFontSize by to derive minimumFontSize. Optional. + * @property {?string} minFontSize The smallest a calculated font size may be. Optional. + */ + /** * Returns a font-size value based on a given font-size preset. * Takes into account fluid typography parameters and attempts to return a css formula depending on available, valid values. * - * @param {Preset} preset - * @param {Object} typographySettings - * @param {boolean} typographySettings.fluid Whether fluid typography is enabled. + * @param {Preset} preset + * @param {Object} typographySettings + * @param {boolean|TypographySettings} typographySettings.fluid Whether fluid typography is enabled, and, optionally, fluid font size options. * * @return {string|*} A font-size value or the value of preset.size. */ @@ -44,7 +54,11 @@ export function getTypographyFontSizeValue( preset, typographySettings ) { return defaultSize; } - if ( true !== typographySettings?.fluid ) { + if ( + ! typographySettings?.fluid || + ( typeof typographySettings?.fluid === 'object' && + Object.keys( typographySettings.fluid ).length === 0 ) + ) { return defaultSize; } @@ -53,10 +67,16 @@ export function getTypographyFontSizeValue( preset, typographySettings ) { return defaultSize; } + const fluidTypographySettings = + typeof typographySettings?.fluid === 'object' + ? typographySettings?.fluid + : {}; + const fluidFontSizeValue = getComputedFluidTypographyValue( { minimumFontSize: preset?.fluid?.min, maximumFontSize: preset?.fluid?.max, fontSize: defaultSize, + minimumFontSizeLimit: fluidTypographySettings?.minFontSize, } ); if ( !! fluidFontSizeValue ) { diff --git a/phpunit/block-supports/typography-test.php b/phpunit/block-supports/typography-test.php index 63fd98d1e524ff..1b9b6f97c63f6e 100644 --- a/phpunit/block-supports/typography-test.php +++ b/phpunit/block-supports/typography-test.php @@ -557,22 +557,18 @@ public function data_generate_font_size_preset_fixtures() { /** * Tests that custom font sizes are converted to fluid values * in inline block supports styles, - * when "settings.typography.fluid" is set to `true`. + * when "settings.typography.fluid" is set to `true` or contains configured values. * * @covers ::gutenberg_register_typography_support * * @dataProvider data_generate_block_supports_font_size_fixtures * - * @param string $font_size_value The block supports custom font size value. - * @param bool $should_use_fluid_typography An override to switch fluid typography "on". Can be used for unit testing. - * @param string $expected_output Expected value of style property from gutenberg_apply_typography_support(). + * @param string $font_size_value The block supports custom font size value. + * @param string $theme_slug A theme slug corresponding to an available test theme. + * @param string $expected_output Expected value of style property from gutenberg_apply_typography_support(). */ - public function test_should_covert_font_sizes_to_fluid_values( $font_size_value, $should_use_fluid_typography, $expected_output ) { - if ( $should_use_fluid_typography ) { - switch_theme( 'block-theme-child-with-fluid-typography' ); - } else { - switch_theme( 'default' ); - } + public function test_should_covert_font_sizes_to_fluid_values( $font_size_value, $theme_slug, $expected_output ) { + switch_theme( $theme_slug ); $this->test_block_name = 'test/font-size-fluid-value'; register_block_type( @@ -614,15 +610,30 @@ public function test_should_covert_font_sizes_to_fluid_values( $font_size_value, */ public function data_generate_block_supports_font_size_fixtures() { return array( - 'default_return_value' => array( - 'font_size_value' => '50px', - 'should_use_fluid_typography' => false, - 'expected_output' => 'font-size:50px;', - ), - 'return_value_with_fluid_typography' => array( - 'font_size_value' => '50px', - 'should_use_fluid_typography' => true, - 'expected_output' => 'font-size:clamp(37.5px, 2.344rem + ((1vw - 7.68px) * 1.502), 50px);', + 'returns value when fluid typography is not active' => array( + 'font_size_value' => '15px', + 'theme_slug' => 'default', + 'expected_output' => 'font-size:15px;', + ), + 'returns clamp value using default config' => array( + 'font_size_value' => '15px', + 'theme_slug' => 'block-theme-child-with-fluid-typography', + 'expected_output' => 'font-size:clamp(14px, 0.875rem + ((1vw - 7.68px) * 0.12), 15px);', + ), + 'returns value when font size <= default min font size bound' => array( + 'font_size_value' => '13px', + 'theme_slug' => 'block-theme-child-with-fluid-typography', + 'expected_output' => 'font-size:13px;', + ), + 'returns clamp value using custom fluid config' => array( + 'font_size_value' => '17px', + 'theme_slug' => 'block-theme-child-with-fluid-typography-config', + 'expected_output' => 'font-size:clamp(16px, 1rem + ((1vw - 7.68px) * 0.12), 17px);', + ), + 'returns value when font size <= custom min font size bound' => array( + 'font_size_value' => '15px', + 'theme_slug' => 'block-theme-child-with-fluid-typography-config', + 'expected_output' => 'font-size:15px;', ), ); } diff --git a/phpunit/data/themedir1/block-theme-child-with-fluid-typography-config/style.css b/phpunit/data/themedir1/block-theme-child-with-fluid-typography-config/style.css new file mode 100644 index 00000000000000..19abbecf86f4cc --- /dev/null +++ b/phpunit/data/themedir1/block-theme-child-with-fluid-typography-config/style.css @@ -0,0 +1,8 @@ +/* +Theme Name: Block Theme Child Theme With Fluid Typography +Theme URI: https://wordpress.org/ +Description: For testing purposes only. +Template: block-theme +Version: 1.0.0 +Text Domain: block-theme-child-with-fluid-typography +*/ diff --git a/phpunit/data/themedir1/block-theme-child-with-fluid-typography-config/theme.json b/phpunit/data/themedir1/block-theme-child-with-fluid-typography-config/theme.json new file mode 100644 index 00000000000000..d0ec32d9caac0a --- /dev/null +++ b/phpunit/data/themedir1/block-theme-child-with-fluid-typography-config/theme.json @@ -0,0 +1,11 @@ +{ + "version": 2, + "settings": { + "appearanceTools": true, + "typography": { + "fluid": { + "minFontSize": "16px" + } + } + } +} diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 1a8aa67b0967b1..d7349262193086 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -353,8 +353,23 @@ "default": true }, "fluid": { - "description": "Opts into fluid typography.", - "type": "boolean" + "description": "Enables fluid typography and allows users to set global fluid typography parameters.", + "oneOf": [ + { + "type": "object", + "properties": { + "minFontSize": { + "description": "Allow users to set a global minimum font size boundary in px, rem or em. Custom font sizes below this value will not be clamped, and all calculated minimum font sizes will be, a at minimum, this value.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "boolean" + } + ], + "default": false }, "letterSpacing": { "description": "Allow users to set custom letter spacing.", From 010361fb106cc6805df7a3e81a3da4625fef7277 Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 16 Dec 2022 00:01:57 +0000 Subject: [PATCH 70/73] Fix: Bug on withSafeTimeouts. (#46595) --- packages/compose/src/higher-order/with-safe-timeout/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/compose/src/higher-order/with-safe-timeout/index.tsx b/packages/compose/src/higher-order/with-safe-timeout/index.tsx index 4056e64ba65e53..d15b8a72d41eab 100644 --- a/packages/compose/src/higher-order/with-safe-timeout/index.tsx +++ b/packages/compose/src/higher-order/with-safe-timeout/index.tsx @@ -59,7 +59,9 @@ const withSafeTimeout = createHigherOrderComponent( clearTimeout( id: number ) { clearTimeout( id ); - this.timeouts.filter( ( timeoutId ) => timeoutId !== id ); + this.timeouts = this.timeouts.filter( + ( timeoutId ) => timeoutId !== id + ); } render() { From 5e00414d2e5157d1c964717c5e38424ba7adf6bb Mon Sep 17 00:00:00 2001 From: Jorge Costa Date: Fri, 16 Dec 2022 00:09:35 +0000 Subject: [PATCH 71/73] Update: Guard against null block in off canvas editor. (#46594) * Update: Guard against null block in off canvas editor. * Change code order to comply with lint rules. --- .../src/components/off-canvas-editor/block.js | 116 +++++++++--------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/packages/block-editor/src/components/off-canvas-editor/block.js b/packages/block-editor/src/components/off-canvas-editor/block.js index 4ad64587796bd7..a3861ef847274f 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block.js +++ b/packages/block-editor/src/components/off-canvas-editor/block.js @@ -95,15 +95,68 @@ function ListViewBlock( { [ clientId ] ); + // If ListView has experimental features related to the Persistent List View, + // only focus the selected list item on mount; otherwise the list would always + // try to steal the focus from the editor canvas. + useEffect( () => { + if ( ! isTreeGridMounted && isSelected ) { + cellRef.current.focus(); + } + }, [] ); + + const onMouseEnter = useCallback( () => { + setIsHovered( true ); + toggleBlockHighlight( clientId, true ); + }, [ clientId, setIsHovered, toggleBlockHighlight ] ); + const onMouseLeave = useCallback( () => { + setIsHovered( false ); + toggleBlockHighlight( clientId, false ); + }, [ clientId, setIsHovered, toggleBlockHighlight ] ); + + const selectEditorBlock = useCallback( + ( event ) => { + selectBlock( event, clientId ); + event.preventDefault(); + }, + [ clientId, selectBlock ] + ); + + const updateSelection = useCallback( + ( newClientId ) => { + selectBlock( undefined, newClientId ); + }, + [ selectBlock ] + ); + + const { isTreeGridMounted, expand, collapse } = useListViewContext(); + + const toggleExpanded = useCallback( + ( event ) => { + // Prevent shift+click from opening link in a new window when toggling. + event.preventDefault(); + event.stopPropagation(); + if ( isExpanded === true ) { + collapse( clientId ); + } else if ( isExpanded === false ) { + expand( clientId ); + } + }, + [ clientId, expand, collapse, isExpanded ] + ); + + const instanceId = useInstanceId( ListViewBlock ); + + if ( ! block ) { + return null; + } + // When a block hides its toolbar it also hides the block settings menu, // since that menu is part of the toolbar in the editor canvas. // List View respects this by also hiding the block settings menu. - const showBlockActions = hasBlockSupport( - block.name, - '__experimentalToolbar', - true - ); - const instanceId = useInstanceId( ListViewBlock ); + const showBlockActions = + !! block && + hasBlockSupport( block.name, '__experimentalToolbar', true ); + const descriptionId = `list-view-block-select-button__${ instanceId }`; const blockPositionDescription = getBlockPositionDescription( position, @@ -142,9 +195,7 @@ function ListViewBlock( { ) : __( 'Edit' ); - const { isTreeGridMounted, expand, collapse } = useListViewContext(); - - const isEditable = block.name !== 'core/page-list-item'; + const isEditable = !! block && block.name !== 'core/page-list-item'; const hasSiblings = siblingBlockCount > 0; const hasRenderedMovers = showBlockMovers && hasSiblings; const moverCellClassName = classnames( @@ -162,53 +213,6 @@ function ListViewBlock( { { 'is-visible': isHovered || isFirstSelectedBlock } ); - // If ListView has experimental features related to the Persistent List View, - // only focus the selected list item on mount; otherwise the list would always - // try to steal the focus from the editor canvas. - useEffect( () => { - if ( ! isTreeGridMounted && isSelected ) { - cellRef.current.focus(); - } - }, [] ); - - const onMouseEnter = useCallback( () => { - setIsHovered( true ); - toggleBlockHighlight( clientId, true ); - }, [ clientId, setIsHovered, toggleBlockHighlight ] ); - const onMouseLeave = useCallback( () => { - setIsHovered( false ); - toggleBlockHighlight( clientId, false ); - }, [ clientId, setIsHovered, toggleBlockHighlight ] ); - - const selectEditorBlock = useCallback( - ( event ) => { - selectBlock( event, clientId ); - event.preventDefault(); - }, - [ clientId, selectBlock ] - ); - - const updateSelection = useCallback( - ( newClientId ) => { - selectBlock( undefined, newClientId ); - }, - [ selectBlock ] - ); - - const toggleExpanded = useCallback( - ( event ) => { - // Prevent shift+click from opening link in a new window when toggling. - event.preventDefault(); - event.stopPropagation(); - if ( isExpanded === true ) { - collapse( clientId ); - } else if ( isExpanded === false ) { - expand( clientId ); - } - }, - [ clientId, expand, collapse, isExpanded ] - ); - let colSpan; if ( hasRenderedMovers ) { colSpan = 2; From 6d9b0b6313aab59bb3f071c5daf1c48fd7ce7bf1 Mon Sep 17 00:00:00 2001 From: Ramon Date: Fri, 16 Dec 2022 13:43:51 +1100 Subject: [PATCH 72/73] Site Editor: Add new 'Push changes to Global Styles' button (#46446) * Rough attempt at 'Push changes to Global Styles' button * Trying to get presets to be copied over to global styles Copy update * Styles needs default value for when only a preset is updated * Show success toast with undo button for now * Disable button when there are no changes to push * Don't create undo levels * Guard against blockGap which isn't supported * Don't show style names to user. They can't be localised * Styling tweaks * Add basic E2E test for the happy path * we need to handle an undefined argument, for example, when only a preset is selected. Getting a few Uncaught SyntaxError: "undefined" is not valid JSON for this function. * Revert package-lock.json * Adjust copy Co-authored-by: Robert Anderson --- .../global-styles/global-styles-provider.js | 16 +- .../src/components/global-styles/utils.js | 11 +- packages/edit-site/src/hooks/index.js | 1 + .../push-changes-to-global-styles/index.js | 162 ++++++++++++++++++ .../push-changes-to-global-styles/style.scss | 4 + packages/edit-site/src/style.scss | 1 + .../site-editor/push-to-global-styles.spec.js | 109 ++++++++++++ 7 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 packages/edit-site/src/hooks/push-changes-to-global-styles/index.js create mode 100644 packages/edit-site/src/hooks/push-changes-to-global-styles/style.scss create mode 100644 test/e2e/specs/site-editor/push-to-global-styles.spec.js diff --git a/packages/edit-site/src/components/global-styles/global-styles-provider.js b/packages/edit-site/src/components/global-styles/global-styles-provider.js index 0ae154c11b91af..0813dc63b2cb86 100644 --- a/packages/edit-site/src/components/global-styles/global-styles-provider.js +++ b/packages/edit-site/src/components/global-styles/global-styles-provider.js @@ -94,7 +94,7 @@ function useGlobalStylesUserConfig() { }, [ settings, styles ] ); const setConfig = useCallback( - ( callback ) => { + ( callback, options = {} ) => { const record = getEditedEntityRecord( 'root', 'globalStyles', @@ -105,10 +105,16 @@ function useGlobalStylesUserConfig() { settings: record?.settings ?? {}, }; const updatedConfig = callback( currentConfig ); - editEntityRecord( 'root', 'globalStyles', globalStylesId, { - styles: cleanEmptyObject( updatedConfig.styles ) || {}, - settings: cleanEmptyObject( updatedConfig.settings ) || {}, - } ); + editEntityRecord( + 'root', + 'globalStyles', + globalStylesId, + { + styles: cleanEmptyObject( updatedConfig.styles ) || {}, + settings: cleanEmptyObject( updatedConfig.settings ) || {}, + }, + options + ); }, [ globalStylesId ] ); diff --git a/packages/edit-site/src/components/global-styles/utils.js b/packages/edit-site/src/components/global-styles/utils.js index 06940becaf7ec1..14f3b868294172 100644 --- a/packages/edit-site/src/components/global-styles/utils.js +++ b/packages/edit-site/src/components/global-styles/utils.js @@ -86,7 +86,7 @@ export const PRESET_METADATA = [ }, ]; -const STYLE_PATH_TO_CSS_VAR_INFIX = { +export const STYLE_PATH_TO_CSS_VAR_INFIX = { 'color.background': 'color', 'color.text': 'color', 'elements.link.color.text': 'color', @@ -100,6 +100,15 @@ const STYLE_PATH_TO_CSS_VAR_INFIX = { 'typography.fontFamily': 'font-family', }; +// A static list of block attributes that store global style preset slugs. +export const STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE = { + 'color.background': 'backgroundColor', + 'color.text': 'textColor', + 'color.gradient': 'gradient', + 'typography.fontSize': 'fontSize', + 'typography.fontFamily': 'fontFamily', +}; + function findInPresetsBy( features, blockName, diff --git a/packages/edit-site/src/hooks/index.js b/packages/edit-site/src/hooks/index.js index 1f7196dd2256c8..513634c55b8f01 100644 --- a/packages/edit-site/src/hooks/index.js +++ b/packages/edit-site/src/hooks/index.js @@ -2,4 +2,5 @@ * Internal dependencies */ import './components'; +import './push-changes-to-global-styles'; import './template-part-edit'; diff --git a/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js b/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js new file mode 100644 index 00000000000000..70b9fa1b02f8ea --- /dev/null +++ b/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js @@ -0,0 +1,162 @@ +/** + * External dependencies + */ +import { get, set } from 'lodash'; + +/** + * WordPress dependencies + */ +import { addFilter } from '@wordpress/hooks'; +import { createHigherOrderComponent } from '@wordpress/compose'; +import { + InspectorAdvancedControls, + store as blockEditorStore, +} from '@wordpress/block-editor'; +import { BaseControl, Button } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import { + __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, + getBlockType, +} from '@wordpress/blocks'; +import { useContext, useMemo, useCallback } from '@wordpress/element'; +import { useDispatch } from '@wordpress/data'; +import { store as noticesStore } from '@wordpress/notices'; + +/** + * Internal dependencies + */ +import { getSupportedGlobalStylesPanels } from '../../components/global-styles/hooks'; +import { GlobalStylesContext } from '../../components/global-styles/context'; +import { + STYLE_PATH_TO_CSS_VAR_INFIX, + STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE, +} from '../../components/global-styles/utils'; + +function getChangesToPush( name, attributes ) { + return getSupportedGlobalStylesPanels( name ).flatMap( ( key ) => { + if ( ! STYLE_PROPERTY[ key ] ) { + return []; + } + const { value: path } = STYLE_PROPERTY[ key ]; + const presetAttributeKey = path.join( '.' ); + const presetAttributeValue = + attributes[ + STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE[ presetAttributeKey ] + ]; + const value = presetAttributeValue + ? `var:preset|${ STYLE_PATH_TO_CSS_VAR_INFIX[ presetAttributeKey ] }|${ presetAttributeValue }` + : get( attributes.style, path ); + return value ? [ { path, value } ] : []; + } ); +} + +function cloneDeep( object ) { + return ! object ? {} : JSON.parse( JSON.stringify( object ) ); +} + +function PushChangesToGlobalStylesControl( { + name, + attributes, + setAttributes, +} ) { + const changes = useMemo( + () => getChangesToPush( name, attributes ), + [ name, attributes ] + ); + + const { user: userConfig, setUserConfig } = + useContext( GlobalStylesContext ); + + const { __unstableMarkNextChangeAsNotPersistent } = + useDispatch( blockEditorStore ); + const { createSuccessNotice } = useDispatch( noticesStore ); + + const pushChanges = useCallback( () => { + if ( changes.length === 0 ) { + return; + } + + const { style: blockStyles } = attributes; + + const newBlockStyles = cloneDeep( blockStyles ); + const newUserConfig = cloneDeep( userConfig ); + + for ( const { path, value } of changes ) { + set( newBlockStyles, path, undefined ); + set( newUserConfig, [ 'styles', 'blocks', name, ...path ], value ); + } + + // @wordpress/core-data doesn't support editing multiple entity types in + // a single undo level. So for now, we disable @wordpress/core-data undo + // tracking and implement our own Undo button in the snackbar + // notification. + __unstableMarkNextChangeAsNotPersistent(); + setAttributes( { style: newBlockStyles } ); + setUserConfig( () => newUserConfig, { undoIgnore: true } ); + + createSuccessNotice( + sprintf( + // translators: %s: Title of the block e.g. 'Heading'. + __( 'Pushed styles to all %s blocks.' ), + getBlockType( name ).title + ), + { + type: 'snackbar', + actions: [ + { + label: __( 'Undo' ), + onClick() { + __unstableMarkNextChangeAsNotPersistent(); + setAttributes( { style: blockStyles } ); + setUserConfig( () => userConfig, { + undoIgnore: true, + } ); + }, + }, + ], + } + ); + }, [ changes, attributes, userConfig, name ] ); + + return ( + + + { __( 'Styles' ) } + + + + ); +} + +const withPushChangesToGlobalStyles = createHigherOrderComponent( + ( BlockEdit ) => ( props ) => + ( + <> + + + + + + ) +); + +addFilter( + 'editor.BlockEdit', + 'core/edit-site/push-changes-to-global-styles', + withPushChangesToGlobalStyles +); diff --git a/packages/edit-site/src/hooks/push-changes-to-global-styles/style.scss b/packages/edit-site/src/hooks/push-changes-to-global-styles/style.scss new file mode 100644 index 00000000000000..33767f4879a401 --- /dev/null +++ b/packages/edit-site/src/hooks/push-changes-to-global-styles/style.scss @@ -0,0 +1,4 @@ +.edit-site-push-changes-to-global-styles-control .components-button { + justify-content: center; + width: 100%; +} diff --git a/packages/edit-site/src/style.scss b/packages/edit-site/src/style.scss index be5b51e16ea084..8b37e2314e237f 100644 --- a/packages/edit-site/src/style.scss +++ b/packages/edit-site/src/style.scss @@ -24,6 +24,7 @@ @import "./components/sidebar-navigation-screen-templates/style.scss"; @import "./components/site-icon/style.scss"; @import "./components/style-book/style.scss"; +@import "./hooks/push-changes-to-global-styles/style.scss"; html #wpadminbar { display: none; diff --git a/test/e2e/specs/site-editor/push-to-global-styles.spec.js b/test/e2e/specs/site-editor/push-to-global-styles.spec.js new file mode 100644 index 00000000000000..ce854f476e4f9d --- /dev/null +++ b/test/e2e/specs/site-editor/push-to-global-styles.spec.js @@ -0,0 +1,109 @@ +/** + * WordPress dependencies + */ +const { + test, + expect, + Editor, +} = require( '@wordpress/e2e-test-utils-playwright' ); + +test.use( { + editor: async ( { page }, use ) => { + await use( new Editor( { page, hasIframe: true } ) ); + }, +} ); + +test.describe( 'Push to Global Styles button', () => { + test.beforeAll( async ( { requestUtils } ) => { + await Promise.all( [ + requestUtils.activateTheme( 'emptytheme' ), + requestUtils.deleteAllTemplates( 'wp_template' ), + requestUtils.deleteAllTemplates( 'wp_template_part' ), + ] ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test.beforeEach( async ( { admin, siteEditor } ) => { + await admin.visitSiteEditor(); + await siteEditor.enterEditMode(); + } ); + + test( 'should apply Heading block styles to all Heading blocks', async ( { + page, + editor, + } ) => { + // Add a Heading block. + await editor.insertBlock( { name: 'core/heading' } ); + await page.keyboard.type( 'A heading' ); + + // Navigate to Styles -> Blocks -> Heading -> Typography + await page.getByRole( 'button', { name: 'Styles' } ).click(); + await page.getByRole( 'button', { name: 'Blocks styles' } ).click(); + await page + .getByRole( 'button', { name: 'Heading block styles' } ) + .click(); + await page.getByRole( 'button', { name: 'Typography styles' } ).click(); + + // Headings should not have uppercase + await expect( + page.getByRole( 'button', { name: 'Uppercase' } ) + ).toHaveAttribute( 'aria-pressed', 'false' ); + + // Go to block settings and open the Advanced panel + await page.getByRole( 'button', { name: 'Settings' } ).click(); + await page.getByRole( 'button', { name: 'Advanced' } ).click(); + + // Push button should be disabled + await expect( + page.getByRole( 'button', { + name: 'Push changes to Global Styles', + } ) + ).toBeDisabled(); + + // Make the Heading block uppercase + await page.getByRole( 'button', { name: 'Uppercase' } ).click(); + + // Push button should now be enabled + await expect( + page.getByRole( 'button', { + name: 'Push changes to Global Styles', + } ) + ).toBeEnabled(); + + // Press the Push button + await page + .getByRole( 'button', { name: 'Push changes to Global Styles' } ) + .click(); + + // Snackbar notification should appear + await expect( + page.getByRole( 'button', { + name: 'Dismiss this notice', + text: 'Pushed styles to all Heading blocks.', + } ) + ).toBeVisible(); + + // Push button should be disabled again + await expect( + page.getByRole( 'button', { + name: 'Push changes to Global Styles', + } ) + ).toBeDisabled(); + + // Navigate again to Styles -> Blocks -> Heading -> Typography + await page.getByRole( 'button', { name: 'Styles' } ).click(); + await page.getByRole( 'button', { name: 'Blocks styles' } ).click(); + await page + .getByRole( 'button', { name: 'Heading block styles' } ) + .click(); + await page.getByRole( 'button', { name: 'Typography styles' } ).click(); + + // Headings should now have uppercase + await expect( + page.getByRole( 'button', { name: 'Uppercase' } ) + ).toHaveAttribute( 'aria-pressed', 'true' ); + } ); +} ); From 0832dba18f055b8752780fdcfae3a4a9349cd5bb Mon Sep 17 00:00:00 2001 From: David Arenas Date: Fri, 16 Dec 2022 06:24:27 +0100 Subject: [PATCH 73/73] Remove unnecessary editorStyle prop in metadata (#46514) --- packages/block-library/src/comments/index.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-library/src/comments/index.php b/packages/block-library/src/comments/index.php index 69044c081c74e0..a5e51cfbf4e793 100644 --- a/packages/block-library/src/comments/index.php +++ b/packages/block-library/src/comments/index.php @@ -202,7 +202,6 @@ function register_legacy_post_comments_block() { 'wp-block-buttons', 'wp-block-button', ), - 'editorStyle' => 'wp-block-post-comments-editor', 'render_callback' => 'render_block_core_comments', 'skip_inner_blocks' => true, );