From 302c1487c0298a75b4a0094e77ec87256f15ccdf Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Mon, 8 Jan 2024 07:31:38 -0600 Subject: [PATCH 01/51] Font Library: unregister font collection (#54701) * remove font collection * add wp_unregister_font_collection function * format php * fix function comment * add tests for unregister_font_collection * removig unused variable * update wording on comments Co-authored-by: Colin Stewart <79332690+costdev@users.noreply.github.com> * keep track of unregistered font collection ids in an array * add tests to assure that get_font_collections returns an empty array if all the font collections were unregistered * fix tests * test the initial empty value of the font library collections * format * simplify unregistering of font collections and add _doing_it_wrong call * add translation domain * adding _doing_it_wrong if you are registering collections with the same id or unregistering a non existing collection id * updating tests * replace 6.4.0 by 6.5.0 in comments Co-authored-by: Vicente Canales <1157901+vcanales@users.noreply.github.com> * update version Co-authored-by: Grant Kinney * consolidate code as only one function to avoid code repetition * create base test case * format php * assertWPError * check that collection was not unregistered by mistake * calling parent test class mehtods * format php --------- Co-authored-by: Colin Stewart <79332690+costdev@users.noreply.github.com> Co-authored-by: Vicente Canales <1157901+vcanales@users.noreply.github.com> Co-authored-by: Grant Kinney --- .../font-library/class-wp-font-library.php | 50 +++++++++++++++-- .../fonts/font-library/font-library.php | 13 +++++ .../fonts/font-library/wpFontLibrary/base.php | 26 +++++++++ .../wpFontLibrary/getFontCollection.php | 8 +-- .../wpFontLibrary/getFontCollections.php | 26 +++------ .../wpFontLibrary/getFontsDir.php | 2 +- .../wpFontLibrary/getMimeTypes.php | 2 +- .../wpFontLibrary/registerFontCollection.php | 6 ++- .../wpFontLibrary/setUploadDir.php | 2 +- .../unregisterFontCollection.php | 54 +++++++++++++++++++ 10 files changed, 156 insertions(+), 33 deletions(-) create mode 100644 phpunit/tests/fonts/font-library/wpFontLibrary/base.php create mode 100644 phpunit/tests/fonts/font-library/wpFontLibrary/unregisterFontCollection.php diff --git a/lib/experimental/fonts/font-library/class-wp-font-library.php b/lib/experimental/fonts/font-library/class-wp-font-library.php index 9320a554e510c..59ec5e93fa787 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-library.php +++ b/lib/experimental/fonts/font-library/class-wp-font-library.php @@ -63,15 +63,57 @@ public static function get_expected_font_mime_types_per_php_version( $php_versio */ public static function register_font_collection( $config ) { $new_collection = new WP_Font_Collection( $config ); - - if ( isset( self::$collections[ $config['id'] ] ) ) { - return new WP_Error( 'font_collection_registration_error', 'Font collection already registered.' ); + if ( self::is_collection_registered( $config['id'] ) ) { + $error_message = sprintf( + /* translators: %s: Font collection id. */ + __( 'Font collection with id: "%s" is already registered.', 'default' ), + $config['id'] + ); + _doing_it_wrong( + __METHOD__, + $error_message, + '6.5.0' + ); + return new WP_Error( 'font_collection_registration_error', $error_message ); } - self::$collections[ $config['id'] ] = $new_collection; return $new_collection; } + /** + * Unregisters a previously registered font collection. + * + * @since 6.5.0 + * + * @param string $collection_id Font collection ID. + * @return bool True if the font collection was unregistered successfully and false otherwise. + */ + public static function unregister_font_collection( $collection_id ) { + if ( ! self::is_collection_registered( $collection_id ) ) { + _doing_it_wrong( + __METHOD__, + /* translators: %s: Font collection id. */ + sprintf( __( 'Font collection "%s" not found.', 'default' ), $collection_id ), + '6.5.0' + ); + return false; + } + unset( self::$collections[ $collection_id ] ); + return true; + } + + /** + * Checks if a font collection is registered. + * + * @since 6.5.0 + * + * @param string $collection_id Font collection ID. + * @return bool True if the font collection is registered and false otherwise. + */ + private static function is_collection_registered( $collection_id ) { + return array_key_exists( $collection_id, self::$collections ); + } + /** * Gets all the font collections available. * diff --git a/lib/experimental/fonts/font-library/font-library.php b/lib/experimental/fonts/font-library/font-library.php index 709f63e9126cb..711a6bb40c282 100644 --- a/lib/experimental/fonts/font-library/font-library.php +++ b/lib/experimental/fonts/font-library/font-library.php @@ -60,6 +60,19 @@ function wp_register_font_collection( $config ) { } } +if ( ! function_exists( 'wp_unregister_font_collection' ) ) { + /** + * Unregisters a font collection from the Font Library. + * + * @since 6.5.0 + * + * @param string $collection_id The font collection ID. + */ + function wp_unregister_font_collection( $collection_id ) { + WP_Font_Library::unregister_font_collection( $collection_id ); + } + +} $default_font_collection = array( 'id' => 'default-font-collection', diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/base.php b/phpunit/tests/fonts/font-library/wpFontLibrary/base.php new file mode 100644 index 0000000000000..e8d970f5b3d39 --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/base.php @@ -0,0 +1,26 @@ +getProperty( 'collections' ); + $property->setAccessible( true ); + $property->setValue( array() ); + } + + public function set_up() { + parent::set_up(); + $this->reset_font_collections(); + } + + public function tear_down() { + parent::tear_down(); + $this->reset_font_collections(); + } +} diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php index bfdb7258fa11a..00d5ca2dcb2e7 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollection.php @@ -10,20 +10,16 @@ * * @covers WP_Font_Library::get_font_collection */ -class Tests_Fonts_WpFontLibrary_GetFontCollection extends WP_UnitTestCase { +class Tests_Fonts_WpFontLibrary_GetFontCollection extends WP_Font_Library_UnitTestCase { - public static function set_up_before_class() { + public function test_should_get_font_collection() { $my_font_collection_config = array( 'id' => 'my-font-collection', 'name' => 'My Font Collection', 'description' => 'Demo about how to a font collection to your WordPress Font Library.', 'src' => path_join( __DIR__, 'my-font-collection-data.json' ), ); - wp_register_font_collection( $my_font_collection_config ); - } - - public function test_should_get_font_collection() { $font_collection = WP_Font_Library::get_font_collection( 'my-font-collection' ); $this->assertInstanceOf( 'WP_Font_Collection', $font_collection ); } diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php index 97e66e64e8716..40eacba8e18c5 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontCollections.php @@ -10,11 +10,13 @@ * * @covers WP_Font_Library::get_font_collections */ -class Tests_Fonts_WpFontLibrary_GetFontCollections extends WP_UnitTestCase { - - public static function set_up_before_class() { - $font_library = new WP_Font_Library(); +class Tests_Fonts_WpFontLibrary_GetFontCollections extends WP_Font_Library_UnitTestCase { + public function test_should_get_an_empty_list() { + $font_collections = WP_Font_Library::get_font_collections(); + $this->assertEmpty( $font_collections, 'Should return an empty array.' ); + } + public function test_should_get_mock_font_collection() { $my_font_collection_config = array( 'id' => 'my-font-collection', 'name' => 'My Font Collection', @@ -22,23 +24,11 @@ public static function set_up_before_class() { 'src' => path_join( __DIR__, 'my-font-collection-data.json' ), ); - $font_library::register_font_collection( $my_font_collection_config ); - } - - public function test_should_get_the_default_font_collection() { - $font_collections = WP_Font_Library::get_font_collections(); - $this->assertArrayHasKey( 'default-font-collection', $font_collections, 'Default Google Fonts collection should be registered' ); - $this->assertInstanceOf( 'WP_Font_Collection', $font_collections['default-font-collection'], 'The value of the array $font_collections[id] should be an instance of WP_Font_Collection class.' ); - } + WP_Font_Library::register_font_collection( $my_font_collection_config ); - public function test_should_get_the_right_number_of_collections() { $font_collections = WP_Font_Library::get_font_collections(); $this->assertNotEmpty( $font_collections, 'Sould return an array of font collections.' ); - $this->assertCount( 2, $font_collections, 'Should return an array with one font collection.' ); - } - - public function test_should_get_mock_font_collection() { - $font_collections = WP_Font_Library::get_font_collections(); + $this->assertCount( 1, $font_collections, 'Should return an array with one font collection.' ); $this->assertArrayHasKey( 'my-font-collection', $font_collections, 'The array should have the key of the registered font collection id.' ); $this->assertInstanceOf( 'WP_Font_Collection', $font_collections['my-font-collection'], 'The value of the array $font_collections[id] should be an instance of WP_Font_Collection class.' ); } diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontsDir.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontsDir.php index 4bbafc55a2147..1200200d7160b 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontsDir.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontsDir.php @@ -10,7 +10,7 @@ * * @covers WP_Font_Library::get_fonts_dir */ -class Tests_Fonts_WpFontLibrary_GetFontsDir extends WP_UnitTestCase { +class Tests_Fonts_WpFontLibrary_GetFontsDir extends WP_Font_Library_UnitTestCase { public function test_get_fonts_dir() { $this->assertStringEndsWith( '/wp-content/fonts', WP_Font_Library::get_fonts_dir() ); diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php index 708134af69e92..485587060f16a 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/getMimeTypes.php @@ -10,7 +10,7 @@ * * @covers WP_Font_Family_Utils::get_expected_font_mime_types_per_php_version */ -class Tests_Fonts_WpFontsFamilyUtils_GetMimeTypes extends WP_UnitTestCase { +class Tests_Fonts_WpFontsFamilyUtils_GetMimeTypes extends WP_Font_Library_UnitTestCase { /** * diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php index 6bc5fbb8161ce..2569830f6bf2a 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/registerFontCollection.php @@ -10,7 +10,7 @@ * * @covers WP_Font_Library::register_font_collection */ -class Tests_Fonts_WpFontLibrary_RegisterFontCollection extends WP_UnitTestCase { +class Tests_Fonts_WpFontLibrary_RegisterFontCollection extends WP_Font_Library_UnitTestCase { public function test_should_register_font_collection() { $config = array( @@ -70,8 +70,10 @@ public function test_should_return_error_if_id_is_repeated() { $collection1 = WP_Font_Library::register_font_collection( $config1 ); $this->assertInstanceOf( 'WP_Font_Collection', $collection1, 'A collection should be registered.' ); + // Expects a _doing_it_wrong notice. + $this->setExpectedIncorrectUsage( 'WP_Font_Library::register_font_collection' ); // Try to register a second collection with same id. $collection2 = WP_Font_Library::register_font_collection( $config2 ); - $this->assertWPError( $collection2, 'Second collection with the same id should fail.' ); + $this->assertWPError( $collection2, 'A WP_Error should be returned.' ); } } diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/setUploadDir.php b/phpunit/tests/fonts/font-library/wpFontLibrary/setUploadDir.php index daa4c84aad900..29d481d8afd6b 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/setUploadDir.php +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/setUploadDir.php @@ -10,7 +10,7 @@ * * @covers WP_Font_Library::set_upload_dir */ -class Tests_Fonts_WpFontLibrary_SetUploadDir extends WP_UnitTestCase { +class Tests_Fonts_WpFontLibrary_SetUploadDir extends WP_Font_Library_UnitTestCase { public function test_should_set_fonts_upload_dir() { $defaults = array( diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/unregisterFontCollection.php b/phpunit/tests/fonts/font-library/wpFontLibrary/unregisterFontCollection.php new file mode 100644 index 0000000000000..e6e16956814fb --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/unregisterFontCollection.php @@ -0,0 +1,54 @@ + 'mock-font-collection-1', + 'name' => 'Mock Collection to be unregistered', + 'description' => 'A mock font collection to be unregistered.', + 'src' => 'my-collection-data.json', + ); + WP_Font_Library::register_font_collection( $config ); + + $config = array( + 'id' => 'mock-font-collection-2', + 'name' => 'Mock Collection', + 'description' => 'A mock font collection.', + 'src' => 'my-mock-data.json', + ); + WP_Font_Library::register_font_collection( $config ); + + // Unregister mock font collection. + WP_Font_Library::unregister_font_collection( 'mock-font-collection-1' ); + $collections = WP_Font_Library::get_font_collections(); + $this->assertArrayNotHasKey( 'mock-font-collection-1', $collections, 'Font collection was not unregistered.' ); + $this->assertArrayHasKey( 'mock-font-collection-2', $collections, 'Font collection was unregistered by mistake.' ); + + // Unregisters remaining mock font collection. + WP_Font_Library::unregister_font_collection( 'mock-font-collection-2' ); + $collections = WP_Font_Library::get_font_collections(); + $this->assertArrayNotHasKey( 'mock-font-collection-2', $collections, 'Mock font collection was not unregistered.' ); + + // Checks that all font collections were unregistered. + $this->assertEmpty( $collections, 'Font collections were not unregistered.' ); + } + + public function unregister_non_existing_collection() { + // Unregisters non existing font collection. + WP_Font_Library::unregister_font_collection( 'non-existing-collection' ); + $collections = WP_Font_Library::get_font_collections(); + $this->assertEmpty( $collections, 'Should not be registered collections.' ); + } +} From 6d4e6ddfccdaebbb75337564c331c402bb3b07d0 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Mon, 8 Jan 2024 08:12:23 -0600 Subject: [PATCH 02/51] Font Library: singularize install font families endpoint (#57569) * singularize install font families endpoint to accept only one font family instead of many in the same request * lint php * frontend changes to send only one font family per install request * restrict the upload of local font files to variants of only one font family * rename function to make it singular * fixing strings for translations * fix permission_callback value updated by mistake --- ...class-wp-rest-font-families-controller.php | 149 ++++----- .../font-library-modal/context.js | 14 +- .../font-library-modal/font-collection.js | 9 +- .../font-library-modal/local-fonts.js | 15 +- .../font-library-modal/resolvers.js | 2 +- .../font-library-modal/utils/index.js | 30 +- .../test/makeFormDataFromFontFamilies.spec.js | 62 ---- .../test/makeFormDataFromFontFamily.spec.js | 58 ++++ .../installFonts.php | 287 ++++++------------ 9 files changed, 258 insertions(+), 368 deletions(-) delete mode 100644 packages/edit-site/src/components/global-styles/font-library-modal/utils/test/makeFormDataFromFontFamilies.spec.js create mode 100644 packages/edit-site/src/components/global-styles/font-library-modal/utils/test/makeFormDataFromFontFamily.spec.js diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php index c92a0d2697f31..0147d80b7bde9 100644 --- a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php +++ b/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php @@ -44,8 +44,7 @@ public function register_routes() { array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), - 'permission_callback' => function () { - return true;}, + 'permission_callback' => array( $this, 'update_font_library_permissions_check' ), ), ) ); @@ -59,7 +58,7 @@ public function register_routes() { 'callback' => array( $this, 'install_fonts' ), 'permission_callback' => array( $this, 'update_font_library_permissions_check' ), 'args' => array( - 'font_families' => array( + 'font_family_settings' => array( 'required' => true, 'type' => 'string', 'validate_callback' => array( $this, 'validate_install_font_families' ), @@ -92,85 +91,61 @@ public function register_routes() { * @param array $files Files to install. * @return array $error_messages Array of error messages. */ - private function get_validation_errors( $font_families, $files ) { + private function get_validation_errors( $font_family_settings, $files ) { $error_messages = array(); - if ( ! is_array( $font_families ) ) { - $error_messages[] = __( 'font_families should be an array of font families.', 'gutenberg' ); + if ( ! is_array( $font_family_settings ) ) { + $error_messages[] = __( 'font_family_settings should be a font family definition.', 'gutenberg' ); return $error_messages; } - // Checks if there is at least one font family. - if ( count( $font_families ) < 1 ) { - $error_messages[] = __( 'font_families should have at least one font family definition.', 'gutenberg' ); + if ( + ! isset( $font_family_settings['slug'] ) || + ! isset( $font_family_settings['name'] ) || + ! isset( $font_family_settings['fontFamily'] ) + ) { + $error_messages[] = __( 'Font family should have slug, name and fontFamily properties defined.', 'gutenberg' ); + return $error_messages; } - for ( $family_index = 0; $family_index < count( $font_families ); $family_index++ ) { - $font_family = $font_families[ $family_index ]; - - if ( - ! isset( $font_family['slug'] ) || - ! isset( $font_family['name'] ) || - ! isset( $font_family['fontFamily'] ) - ) { - $error_messages[] = sprintf( - // translators: 1: font family index. - __( 'Font family [%s] should have slug, name and fontFamily properties defined.', 'gutenberg' ), - $family_index - ); + if ( isset( $font_family_settings['fontFace'] ) ) { + if ( ! is_array( $font_family_settings['fontFace'] ) ) { + $error_messages[] = __( 'Font family should have fontFace property defined as an array.', 'gutenberg' ); } - if ( isset( $font_family['fontFace'] ) ) { - if ( ! is_array( $font_family['fontFace'] ) ) { - $error_messages[] = sprintf( - // translators: 1: font family index. - __( 'Font family [%s] should have fontFace property defined as an array.', 'gutenberg' ), - $family_index - ); - continue; - } + if ( count( $font_family_settings['fontFace'] ) < 1 ) { + $error_messages[] = __( 'Font family should have at least one font face definition.', 'gutenberg' ); + } - if ( count( $font_family['fontFace'] ) < 1 ) { - $error_messages[] = sprintf( - // translators: 1: font family index. - __( 'Font family [%s] should have at least one font face definition.', 'gutenberg' ), - $family_index - ); - } + if ( ! empty( $font_family_settings['fontFace'] ) ) { + for ( $face_index = 0; $face_index < count( $font_family_settings['fontFace'] ); $face_index++ ) { - if ( ! empty( $font_family['fontFace'] ) ) { - for ( $face_index = 0; $face_index < count( $font_family['fontFace'] ); $face_index++ ) { + $font_face = $font_family_settings['fontFace'][ $face_index ]; + if ( ! isset( $font_face['fontWeight'] ) || ! isset( $font_face['fontStyle'] ) ) { + $error_messages[] = sprintf( + // translators: font face index. + __( 'Font family Font face [%1$s] should have fontWeight and fontStyle properties defined.', 'gutenberg' ), + $face_index + ); + } - $font_face = $font_family['fontFace'][ $face_index ]; - if ( ! isset( $font_face['fontWeight'] ) || ! isset( $font_face['fontStyle'] ) ) { - $error_messages[] = sprintf( - // translators: 1: font family index, 2: font face index. - __( 'Font family [%1$s] Font face [%2$s] should have fontWeight and fontStyle properties defined.', 'gutenberg' ), - $family_index, - $face_index - ); - } + if ( isset( $font_face['downloadFromUrl'] ) && isset( $font_face['uploadedFile'] ) ) { + $error_messages[] = sprintf( + // translators: font face index. + __( 'Font family Font face [%1$s] should have only one of the downloadFromUrl or uploadedFile properties defined and not both.', 'gutenberg' ), + $face_index + ); + } - if ( isset( $font_face['downloadFromUrl'] ) && isset( $font_face['uploadedFile'] ) ) { + if ( isset( $font_face['uploadedFile'] ) ) { + if ( ! isset( $files[ $font_face['uploadedFile'] ] ) ) { $error_messages[] = sprintf( - // translators: 1: font family index, 2: font face index. - __( 'Font family [%1$s] Font face [%2$s] should have only one of the downloadFromUrl or uploadedFile properties defined and not both.', 'gutenberg' ), - $family_index, + // translators: font face index. + __( 'Font family Font face [%1$s] file is not defined in the request files.', 'gutenberg' ), $face_index ); } - - if ( isset( $font_face['uploadedFile'] ) ) { - if ( ! isset( $files[ $font_face['uploadedFile'] ] ) ) { - $error_messages[] = sprintf( - // translators: 1: font family index, 2: font face index. - __( 'Font family [%1$s] Font face [%2$s] file is not defined in the request files.', 'gutenberg' ), - $family_index, - $face_index - ); - } - } } } } @@ -189,9 +164,9 @@ private function get_validation_errors( $font_families, $files ) { * @return true|WP_Error True if the parameter is valid, WP_Error otherwise. */ public function validate_install_font_families( $param, $request ) { - $font_families = json_decode( $param, true ); - $files = $request->get_file_params(); - $error_messages = $this->get_validation_errors( $font_families, $files ); + $font_family_settings = json_decode( $param, true ); + $files = $request->get_file_params(); + $error_messages = $this->get_validation_errors( $font_family_settings, $files ); if ( empty( $error_messages ) ) { return true; @@ -327,17 +302,15 @@ private function has_write_permission() { * * @since 6.5.0 * - * @param array[] $font_families Font families to install. + * @param array[] $font_family_settings Font family definition. * @return bool Whether the request needs write permissions. */ - private function needs_write_permission( $font_families ) { - foreach ( $font_families as $font ) { - if ( isset( $font['fontFace'] ) ) { - foreach ( $font['fontFace'] as $face ) { - // If the font is being downloaded from a URL or uploaded, it needs write permissions. - if ( isset( $face['downloadFromUrl'] ) || isset( $face['uploadedFile'] ) ) { - return true; - } + private function needs_write_permission( $font_family_settings ) { + if ( isset( $font_family_settings['fontFace'] ) ) { + foreach ( $font_family_settings['fontFace'] as $face ) { + // If the font is being downloaded from a URL or uploaded, it needs write permissions. + if ( isset( $face['downloadFromUrl'] ) || isset( $face['uploadedFile'] ) ) { + return true; } } } @@ -358,20 +331,20 @@ private function needs_write_permission( $font_families ) { */ public function install_fonts( $request ) { // Get new fonts to install. - $fonts_param = $request->get_param( 'font_families' ); + $font_family_settings = $request->get_param( 'font_family_settings' ); /* * As this is receiving form data, the font families are encoded as a string. * The form data is used because local fonts need to use that format to * attach the files in the request. */ - $fonts_to_install = json_decode( $fonts_param, true ); + $font_family_settings = json_decode( $font_family_settings, true ); $successes = array(); $errors = array(); $response_status = 200; - if ( empty( $fonts_to_install ) ) { + if ( empty( $font_family_settings ) ) { $errors[] = new WP_Error( 'no_fonts_to_install', __( 'No fonts to install', 'gutenberg' ) @@ -379,7 +352,7 @@ public function install_fonts( $request ) { $response_status = 400; } - if ( $this->needs_write_permission( $fonts_to_install ) ) { + if ( $this->needs_write_permission( $font_family_settings ) ) { $upload_dir = WP_Font_Library::get_fonts_dir(); if ( ! $this->has_upload_directory() ) { if ( ! wp_mkdir_p( $upload_dir ) ) { @@ -415,15 +388,13 @@ public function install_fonts( $request ) { } // Get uploaded files (used when installing local fonts). - $files = $request->get_file_params(); - foreach ( $fonts_to_install as $font_data ) { - $font = new WP_Font_Family( $font_data ); - $result = $font->install( $files ); - if ( is_wp_error( $result ) ) { - $errors[] = $result; - } else { - $successes[] = $result; - } + $files = $request->get_file_params(); + $font = new WP_Font_Family( $font_family_settings ); + $result = $font->install( $files ); + if ( is_wp_error( $result ) ) { + $errors[] = $result; + } else { + $successes[] = $result; } $data = array( diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/context.js b/packages/edit-site/src/components/global-styles/font-library-modal/context.js index 58b8621adcf0c..e0749845788d6 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/context.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/context.js @@ -14,7 +14,7 @@ import { * Internal dependencies */ import { - fetchInstallFonts, + fetchInstallFont, fetchUninstallFonts, fetchFontCollections, fetchFontCollection, @@ -26,7 +26,7 @@ import { mergeFontFamilies, loadFontFaceInBrowser, getDisplaySrcFromFontFace, - makeFormDataFromFontFamilies, + makeFormDataFromFontFamily, } from './utils'; import { toggleFont } from './utils/toggleFont'; import getIntersectingFontFaces from './utils/get-intersecting-font-faces'; @@ -192,19 +192,19 @@ function FontLibraryProvider( { children } ) { return getActivatedFontsOutline( source )[ slug ] || []; }; - async function installFonts( fonts ) { + async function installFont( font ) { setIsInstalling( true ); try { // Prepare formData to install. - const formData = makeFormDataFromFontFamilies( fonts ); + const formData = makeFormDataFromFontFamily( font ); // Install the fonts (upload the font files to the server and create the post in the database). - const response = await fetchInstallFonts( formData ); + const response = await fetchInstallFont( formData ); const fontsInstalled = response?.successes || []; // Get intersecting font faces between the fonts we tried to installed and the fonts that were installed // (to avoid activating a non installed font). const fontToBeActivated = getIntersectingFontFaces( fontsInstalled, - fonts + [ font ] ); // Activate the font families (add the font families to the global styles). activateCustomFontFamilies( fontToBeActivated ); @@ -358,7 +358,7 @@ function FontLibraryProvider( { children } ) { isFontActivated, getFontFacesActivated, loadFontFaceAsset, - installFonts, + installFont, uninstallFont, toggleActivateFont, getAvailableFontsOutline, diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js b/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js index 062ad232e3ca9..fc39e2e009653 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/font-collection.js @@ -54,7 +54,7 @@ function FontCollection( { id } ) { const [ renderConfirmDialog, setRenderConfirmDialog ] = useState( requiresPermission && ! getGoogleFontsPermissionFromStorage() ); - const { collections, getFontCollection, installFonts } = + const { collections, getFontCollection, installFont } = useContext( FontLibraryContext ); const selectedCollection = collections.find( ( collection ) => collection.id === id @@ -92,6 +92,11 @@ function FontCollection( { id } ) { setNotice( null ); }, [ id ] ); + useEffect( () => { + // If the selected fonts change, reset the selected fonts to install + setFontsToInstall( [] ); + }, [ selectedFont ] ); + // Reset notice after 5 seconds useEffect( () => { if ( notice && notice?.duration !== 0 ) { @@ -149,7 +154,7 @@ function FontCollection( { id } ) { }; const handleInstall = async () => { - const response = await installFonts( fontsToInstall ); + const response = await installFont( fontsToInstall[ 0 ] ); const installNotice = getNoticeFromInstallResponse( response ); setNotice( installNotice ); resetFontsToInstall(); diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/local-fonts.js b/packages/edit-site/src/components/global-styles/font-library-modal/local-fonts.js index 4030dcfb69a77..d4221b420cb61 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/local-fonts.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/local-fonts.js @@ -29,7 +29,7 @@ import { unlock } from '../../../lock-unlock'; const { ProgressBar } = unlock( componentsPrivateApis ); function LocalFonts() { - const { installFonts } = useContext( FontLibraryContext ); + const { installFont } = useContext( FontLibraryContext ); const [ notice, setNotice ] = useState( null ); const [ isUploading, setIsUploading ] = useState( false ); const supportedFormats = @@ -153,7 +153,18 @@ function LocalFonts() { */ const handleInstall = async ( fontFaces ) => { const fontFamilies = makeFamiliesFromFaces( fontFaces ); - const response = await installFonts( fontFamilies ); + + if ( fontFamilies.length > 1 ) { + setNotice( { + type: 'error', + message: __( + 'Variants from only one font family can be uploaded at a time.' + ), + } ); + return; + } + + const response = await installFont( fontFamilies[ 0 ] ); const installNotice = getNoticeFromInstallResponse( response ); setNotice( installNotice ); setIsUploading( false ); diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js b/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js index 0ab4a7ba74224..2e7f413a6fa45 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/resolvers.js @@ -7,7 +7,7 @@ */ import apiFetch from '@wordpress/api-fetch'; -export async function fetchInstallFonts( data ) { +export async function fetchInstallFont( data ) { const config = { path: '/wp/v2/font-families', method: 'POST', diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/utils/index.js b/packages/edit-site/src/components/global-styles/font-library-modal/utils/index.js index 69db09d49a0ce..2874dd446efb4 100644 --- a/packages/edit-site/src/components/global-styles/font-library-modal/utils/index.js +++ b/packages/edit-site/src/components/global-styles/font-library-modal/utils/index.js @@ -130,16 +130,21 @@ export function getDisplaySrcFromFontFace( input, urlPrefix ) { return src; } -export function makeFormDataFromFontFamilies( fontFamilies ) { +export function makeFormDataFromFontFamily( fontFamily ) { const formData = new FormData(); - const newFontFamilies = fontFamilies.map( ( family, familyIndex ) => { - const { kebabCase } = unlock( componentsPrivateApis ); - family.slug = kebabCase( family.slug ); - if ( family?.fontFace ) { - family.fontFace = family.fontFace.map( ( face, faceIndex ) => { + const { kebabCase } = unlock( componentsPrivateApis ); + + const newFontFamily = { + ...fontFamily, + slug: kebabCase( fontFamily.slug ), + }; + + if ( newFontFamily?.fontFace ) { + const newFontFaces = newFontFamily.fontFace.map( + ( face, faceIndex ) => { if ( face.file ) { // Slugified file name because the it might contain spaces or characters treated differently on the server. - const fileId = `file-${ familyIndex }-${ faceIndex }`; + const fileId = `file-${ faceIndex }`; // Add the files to the formData formData.append( fileId, face.file, face.file.name ); // remove the file object from the face object the file is referenced by the uploadedFile key @@ -151,10 +156,11 @@ export function makeFormDataFromFontFamilies( fontFamilies ) { return newFace; } return face; - } ); - } - return family; - } ); - formData.append( 'font_families', JSON.stringify( newFontFamilies ) ); + } + ); + newFontFamily.fontFace = newFontFaces; + } + + formData.append( 'font_family_settings', JSON.stringify( newFontFamily ) ); return formData; } diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/utils/test/makeFormDataFromFontFamilies.spec.js b/packages/edit-site/src/components/global-styles/font-library-modal/utils/test/makeFormDataFromFontFamilies.spec.js deleted file mode 100644 index 4adae7889cc5e..0000000000000 --- a/packages/edit-site/src/components/global-styles/font-library-modal/utils/test/makeFormDataFromFontFamilies.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Internal dependencies - */ -import { makeFormDataFromFontFamilies } from '../index'; - -/* global File */ - -describe( 'makeFormDataFromFontFamilies', () => { - it( 'should process fontFamilies and return FormData', () => { - const mockFontFamilies = [ - { - slug: 'bebas', - name: 'Bebas', - fontFamily: 'Bebas', - fontFace: [ - { - file: new File( [ 'content' ], 'test-font1.woff2' ), - fontWeight: '500', - fontStyle: 'normal', - }, - { - file: new File( [ 'content' ], 'test-font2.woff2' ), - fontWeight: '400', - fontStyle: 'normal', - }, - ], - }, - ]; - - const formData = makeFormDataFromFontFamilies( mockFontFamilies ); - - expect( formData instanceof FormData ).toBeTruthy(); - - // Check if files are added correctly - expect( formData.get( 'file-0-0' ).name ).toBe( 'test-font1.woff2' ); - expect( formData.get( 'file-0-1' ).name ).toBe( 'test-font2.woff2' ); - - // Check if 'fontFamilies' key in FormData is correct - const expectedFontFamilies = [ - { - fontFace: [ - { - fontWeight: '500', - fontStyle: 'normal', - uploadedFile: 'file-0-0', - }, - { - fontWeight: '400', - fontStyle: 'normal', - uploadedFile: 'file-0-1', - }, - ], - slug: 'bebas', - name: 'Bebas', - fontFamily: 'Bebas', - }, - ]; - expect( JSON.parse( formData.get( 'font_families' ) ) ).toEqual( - expectedFontFamilies - ); - } ); -} ); diff --git a/packages/edit-site/src/components/global-styles/font-library-modal/utils/test/makeFormDataFromFontFamily.spec.js b/packages/edit-site/src/components/global-styles/font-library-modal/utils/test/makeFormDataFromFontFamily.spec.js new file mode 100644 index 0000000000000..9f38903c89759 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/font-library-modal/utils/test/makeFormDataFromFontFamily.spec.js @@ -0,0 +1,58 @@ +/** + * Internal dependencies + */ +import { makeFormDataFromFontFamily } from '../index'; + +/* global File */ + +describe( 'makeFormDataFromFontFamily', () => { + it( 'should process fontFamilies and return FormData', () => { + const mockFontFamily = { + slug: 'bebas', + name: 'Bebas', + fontFamily: 'Bebas', + fontFace: [ + { + file: new File( [ 'content' ], 'test-font1.woff2' ), + fontWeight: '500', + fontStyle: 'normal', + }, + { + file: new File( [ 'content' ], 'test-font2.woff2' ), + fontWeight: '400', + fontStyle: 'normal', + }, + ], + }; + + const formData = makeFormDataFromFontFamily( mockFontFamily ); + + expect( formData instanceof FormData ).toBeTruthy(); + + // Check if files are added correctly + expect( formData.get( 'file-0' ).name ).toBe( 'test-font1.woff2' ); + expect( formData.get( 'file-1' ).name ).toBe( 'test-font2.woff2' ); + + // Check if 'fontFamilies' key in FormData is correct + const expectedFontFamily = { + fontFace: [ + { + fontWeight: '500', + fontStyle: 'normal', + uploadedFile: 'file-0', + }, + { + fontWeight: '400', + fontStyle: 'normal', + uploadedFile: 'file-1', + }, + ], + slug: 'bebas', + name: 'Bebas', + fontFamily: 'Bebas', + }; + expect( JSON.parse( formData.get( 'font_family_settings' ) ) ).toEqual( + expectedFontFamily + ); + } ); +} ); diff --git a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/installFonts.php b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/installFonts.php index d35022306f4e6..98c1cb6e13fe5 100644 --- a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/installFonts.php +++ b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/installFonts.php @@ -21,10 +21,10 @@ class Tests_Fonts_WPRESTFontFamiliesController_InstallFonts extends WP_REST_Font * @param array $files Font files to install. * @param array $expected_response Expected response data. */ - public function test_install_fonts( $font_families, $files, $expected_response ) { - $install_request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); - $font_families_json = json_encode( $font_families ); - $install_request->set_param( 'font_families', $font_families_json ); + public function test_install_fonts( $font_family_settings, $files, $expected_response ) { + $install_request = new WP_REST_Request( 'POST', '/wp/v2/font-families' ); + $font_family_json = json_encode( $font_family_settings ); + $install_request->set_param( 'font_family_settings', $font_family_json ); $install_request->set_file_params( $files ); $response = rest_get_server()->dispatch( $install_request ); $data = $response->get_data(); @@ -68,38 +68,22 @@ public function data_install_fonts() { return array( 'google_fonts_to_download' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - 'downloadFromUrl' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - ), - ), - ), - array( - 'fontFamily' => 'Montserrat', - 'slug' => 'montserrat', - 'name' => 'Montserrat', - 'fontFace' => array( - array( - 'fontFamily' => 'Montserrat', - 'fontStyle' => 'normal', - 'fontWeight' => '100', - 'src' => 'http://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Uw-Y3tcoqK5.ttf', - 'downloadFromUrl' => 'http://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Uw-Y3tcoqK5.ttf', - ), + 'font_family_settings' => array( + 'fontFamily' => 'Piazzolla', + 'slug' => 'piazzolla', + 'name' => 'Piazzolla', + 'fontFace' => array( + array( + 'fontFamily' => 'Piazzolla', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', + 'downloadFromUrl' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', ), ), ), - 'files' => array(), - 'expected_response' => array( + 'files' => array(), + 'expected_response' => array( 'successes' => array( array( 'fontFamily' => 'Piazzolla', @@ -114,55 +98,27 @@ public function data_install_fonts() { ), ), ), - array( - 'fontFamily' => 'Montserrat', - 'slug' => 'montserrat', - 'name' => 'Montserrat', - 'fontFace' => array( - array( - 'fontFamily' => 'Montserrat', - 'fontStyle' => 'normal', - 'fontWeight' => '100', - 'src' => '/wp-content/fonts/montserrat_normal_100.ttf', - ), - ), - ), ), 'errors' => array(), ), ), 'google_fonts_to_use_as_is' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - ), - ), - ), - array( - 'fontFamily' => 'Montserrat', - 'slug' => 'montserrat', - 'name' => 'Montserrat', - 'fontFace' => array( - array( - 'fontFamily' => 'Montserrat', - 'fontStyle' => 'normal', - 'fontWeight' => '100', - 'src' => 'http://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Uw-Y3tcoqK5.ttf', - ), + 'font_family_settings' => array( + 'fontFamily' => 'Piazzolla', + 'slug' => 'piazzolla', + 'name' => 'Piazzolla', + 'fontFace' => array( + array( + 'fontFamily' => 'Piazzolla', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', ), ), ), - 'files' => array(), - 'expected_response' => array( + 'files' => array(), + 'expected_response' => array( 'successes' => array( array( 'fontFamily' => 'Piazzolla', @@ -177,35 +133,19 @@ public function data_install_fonts() { ), ), ), - array( - 'fontFamily' => 'Montserrat', - 'slug' => 'montserrat', - 'name' => 'Montserrat', - 'fontFace' => array( - array( - 'fontFamily' => 'Montserrat', - 'fontStyle' => 'normal', - 'fontWeight' => '100', - 'src' => 'http://fonts.gstatic.com/s/montserrat/v25/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Uw-Y3tcoqK5.ttf', - - ), - ), - ), ), 'errors' => array(), ), ), 'fonts_without_font_faces' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Arial', - 'slug' => 'arial', - 'name' => 'Arial', - ), + 'font_family_settings' => array( + 'fontFamily' => 'Arial', + 'slug' => 'arial', + 'name' => 'Arial', ), - 'files' => array(), - 'expected_response' => array( + 'files' => array(), + 'expected_response' => array( 'successes' => array( array( 'fontFamily' => 'Arial', @@ -218,35 +158,20 @@ public function data_install_fonts() { ), 'fonts_with_local_fonts_assets' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'uploadedFile' => 'files0', - ), - ), - ), - array( - 'fontFamily' => 'Montserrat', - 'slug' => 'montserrat', - 'name' => 'Montserrat', - 'fontFace' => array( - array( - 'fontFamily' => 'Montserrat', - 'fontStyle' => 'normal', - 'fontWeight' => '100', - 'uploadedFile' => 'files1', - ), + 'font_family_settings' => array( + 'fontFamily' => 'Piazzolla', + 'slug' => 'piazzolla', + 'name' => 'Piazzolla', + 'fontFace' => array( + array( + 'fontFamily' => 'Piazzolla', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + 'uploadedFile' => 'files0', ), ), ), - 'files' => array( + 'files' => array( 'files0' => array( 'name' => 'piazzola1.ttf', 'type' => 'font/ttf', @@ -262,7 +187,7 @@ public function data_install_fonts() { 'size' => 123, ), ), - 'expected_response' => array( + 'expected_response' => array( 'successes' => array( array( 'fontFamily' => 'Piazzolla', @@ -277,20 +202,6 @@ public function data_install_fonts() { ), ), ), - array( - 'fontFamily' => 'Montserrat', - 'slug' => 'montserrat', - 'name' => 'Montserrat', - 'fontFace' => array( - array( - 'fontFamily' => 'Montserrat', - 'fontStyle' => 'normal', - 'fontWeight' => '100', - 'src' => '/wp-content/fonts/montserrat_normal_100.ttf', - ), - ), - ), - ), 'errors' => array(), ), @@ -325,15 +236,15 @@ public function data_install_with_improper_inputs() { return array( 'not a font families array' => array( - 'font_families' => 'This is not an array', + 'font_family_settings' => 'This is not an array', ), 'empty array' => array( - 'font_families' => array(), + 'font_family_settings' => array(), ), 'without slug' => array( - 'font_families' => array( + 'font_family_settings' => array( array( 'fontFamily' => 'Piazzolla', 'name' => 'Piazzolla', @@ -342,63 +253,55 @@ public function data_install_with_improper_inputs() { ), 'with improper font face property' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - 'slug' => 'piazzolla', - 'fontFace' => 'This is not an array', - ), + 'font_family_settings' => array( + 'fontFamily' => 'Piazzolla', + 'name' => 'Piazzolla', + 'slug' => 'piazzolla', + 'fontFace' => 'This is not an array', ), ), 'with empty font face property' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - 'slug' => 'piazzolla', - 'fontFace' => array(), - ), + 'font_family_settings' => array( + 'fontFamily' => 'Piazzolla', + 'name' => 'Piazzolla', + 'slug' => 'piazzolla', + 'fontFace' => array(), ), ), 'fontface referencing uploaded file without uploaded files' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - 'slug' => 'piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'uploadedFile' => 'files0', - ), + 'font_family_settings' => array( + 'fontFamily' => 'Piazzolla', + 'name' => 'Piazzolla', + 'slug' => 'piazzolla', + 'fontFace' => array( + array( + 'fontFamily' => 'Piazzolla', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + 'uploadedFile' => 'files0', ), ), ), - 'files' => array(), + 'files' => array(), ), 'fontface referencing uploaded file without uploaded files' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Piazzolla', - 'name' => 'Piazzolla', - 'slug' => 'piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'uploadedFile' => 'files666', - ), + 'font_family_settings' => array( + 'fontFamily' => 'Piazzolla', + 'name' => 'Piazzolla', + 'slug' => 'piazzolla', + 'fontFace' => array( + array( + 'fontFamily' => 'Piazzolla', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + 'uploadedFile' => 'files666', ), ), ), - 'files' => array( + 'files' => array( 'files0' => array( 'name' => 'piazzola1.ttf', 'type' => 'font/ttf', @@ -410,20 +313,18 @@ public function data_install_with_improper_inputs() { ), 'fontface with incompatible properties (downloadFromUrl and uploadedFile together)' => array( - 'font_families' => array( - array( - 'fontFamily' => 'Piazzolla', - 'slug' => 'piazzolla', - 'name' => 'Piazzolla', - 'fontFace' => array( - array( - 'fontFamily' => 'Piazzolla', - 'fontStyle' => 'normal', - 'fontWeight' => '400', - 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - 'downloadFromUrl' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', - 'uploadedFile' => 'files0', - ), + 'font_family_settings' => array( + 'fontFamily' => 'Piazzolla', + 'slug' => 'piazzolla', + 'name' => 'Piazzolla', + 'fontFace' => array( + array( + 'fontFamily' => 'Piazzolla', + 'fontStyle' => 'normal', + 'fontWeight' => '400', + 'src' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', + 'downloadFromUrl' => 'http://fonts.gstatic.com/s/piazzolla/v33/N0b72SlTPu5rIkWIZjVgI-TckS03oGpPETyEJ88Rbvi0_TzOzKcQhZqx3gX9BRy5m5M.ttf', + 'uploadedFile' => 'files0', ), ), ), From 73474eba696b897627a0f7b3dd84a646e294e690 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:21:44 +0900 Subject: [PATCH 03/51] Add Template Modal: Update scroll related layout (#57617) --- packages/edit-site/src/components/add-new-template/style.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 b1c2b669e24ce..4881745c92afa 100644 --- a/packages/edit-site/src/components/add-new-template/style.scss +++ b/packages/edit-site/src/components/add-new-template/style.scss @@ -39,7 +39,8 @@ .edit-site-custom-template-modal__suggestions_list { @include break-small() { - overflow: scroll; + max-height: $grid-unit-70 * 4; // Height of four buttons + overflow-y: auto; } &__list-item { From 1b5ebca728074569e1ddff9928badcc882627e1c Mon Sep 17 00:00:00 2001 From: James Koster Date: Mon, 8 Jan 2024 15:53:35 +0000 Subject: [PATCH 04/51] Data views: Make title display in grid views consistent (#57553) --- packages/dataviews/src/style.scss | 26 ++++++++++++------- .../edit-site/src/components/list/style.scss | 5 ---- .../src/components/page-pages/index.js | 2 +- .../src/components/page-pages/style.scss | 6 ----- .../page-patterns/dataviews-patterns.js | 5 +++- .../src/components/page-templates/index.js | 2 +- 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index edf1500d2cc5a..5a2bccdef68f2 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -149,10 +149,22 @@ } .dataviews-view-grid__card { - h3 { // Todo: A better way to target this - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + .dataviews-view-grid__primary-field { + .dataviews-view-grid__title-field { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; + font-size: $default-font-size; + width: 100%; + } + + .dataviews-view-grid__title-field a, + button.dataviews-view-grid__title-field { + font-weight: 500; + color: $gray-900; + text-decoration: none; + } } } @@ -173,12 +185,6 @@ .dataviews-view-grid__primary-field { min-height: $grid-unit-30; - - a { - color: $gray-900; - text-decoration: none; - font-weight: 500; - } } .dataviews-view-grid__fields { diff --git a/packages/edit-site/src/components/list/style.scss b/packages/edit-site/src/components/list/style.scss index 9d6e0f0f1d6e2..cfc65252c8e68 100644 --- a/packages/edit-site/src/components/list/style.scss +++ b/packages/edit-site/src/components/list/style.scss @@ -186,8 +186,3 @@ display: block; color: $gray-700; } - -.edit-site-list-title__customized-info { - font-size: $default-font-size; - font-weight: 500; -} diff --git a/packages/edit-site/src/components/page-pages/index.js b/packages/edit-site/src/components/page-pages/index.js index 164b4daf8603c..8729922faabea 100644 --- a/packages/edit-site/src/components/page-pages/index.js +++ b/packages/edit-site/src/components/page-pages/index.js @@ -222,7 +222,7 @@ export default function PagePages() { { [ LAYOUT_TABLE, LAYOUT_GRID ].includes( view.type diff --git a/packages/edit-site/src/components/page-pages/style.scss b/packages/edit-site/src/components/page-pages/style.scss index 35ac8273dc555..933fdadb8d070 100644 --- a/packages/edit-site/src/components/page-pages/style.scss +++ b/packages/edit-site/src/components/page-pages/style.scss @@ -3,9 +3,3 @@ width: $grid-unit-40; height: $grid-unit-40; } - - -.edit-site-page-pages__list-view-title-field { - font-size: $default-font-size; - font-weight: 500; -} diff --git a/packages/edit-site/src/components/page-patterns/dataviews-patterns.js b/packages/edit-site/src/components/page-patterns/dataviews-patterns.js index bf18e0ffed111..ad474d882cfcf 100644 --- a/packages/edit-site/src/components/page-patterns/dataviews-patterns.js +++ b/packages/edit-site/src/components/page-patterns/dataviews-patterns.js @@ -199,7 +199,9 @@ function Title( { item, categoryId } ) { ) } { item.type === PATTERN_TYPES.theme ? ( - item.title + + { item.title } + ) : ( diff --git a/packages/edit-site/src/components/page-templates/index.js b/packages/edit-site/src/components/page-templates/index.js index 1aaf1e153d0c5..c0e0289311db6 100644 --- a/packages/edit-site/src/components/page-templates/index.js +++ b/packages/edit-site/src/components/page-templates/index.js @@ -103,7 +103,7 @@ function TemplateTitle( { item, viewType } ) { return ( - + Date: Mon, 8 Jan 2024 16:01:18 +0000 Subject: [PATCH 05/51] Update Table layout design details (#57644) --- packages/dataviews/src/pagination.js | 4 +-- packages/dataviews/src/style.scss | 30 +++++++++++++++---- packages/dataviews/src/view-table.js | 8 +++-- .../src/components/page-pages/style.scss | 5 ++-- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/packages/dataviews/src/pagination.js b/packages/dataviews/src/pagination.js index 21aeda8a602a1..2c9cade42d89b 100644 --- a/packages/dataviews/src/pagination.js +++ b/packages/dataviews/src/pagination.js @@ -23,8 +23,8 @@ const Pagination = memo( function Pagination( { totalPages !== 1 && ( diff --git a/packages/dataviews/src/style.scss b/packages/dataviews/src/style.scss index 5a2bccdef68f2..049f006dd97bd 100644 --- a/packages/dataviews/src/style.scss +++ b/packages/dataviews/src/style.scss @@ -88,6 +88,11 @@ td:first-child, th:first-child { padding-left: $grid-unit-40; + + .dataviews-table-header-button, + .dataviews-table-header { + margin-left: - #{$grid-unit-10}; + } } td:last-child, @@ -112,18 +117,27 @@ th { position: sticky; top: -1px; - background-color: lighten($gray-100, 4%); + background-color: $white; box-shadow: inset 0 -#{$border-width} 0 $gray-100; - border-top: 1px solid $gray-100; - padding-top: $grid-unit-05; - padding-bottom: $grid-unit-05; + padding-top: $grid-unit-10; + padding-bottom: $grid-unit-10; z-index: 1; + font-size: 11px; + text-transform: uppercase; + font-weight: 500; + padding-left: $grid-unit-05; } } .dataviews-table-header-button { - padding: 0; - gap: $grid-unit-05; + padding: $grid-unit-05 $grid-unit-10; + font-size: 11px; + text-transform: uppercase; + font-weight: 500; + + &:not(:hover) { + color: $gray-900; + } span { speak: none; @@ -133,6 +147,10 @@ } } } + + .dataviews-table-header { + padding-left: $grid-unit-05; + } } .dataviews-grid-view { diff --git a/packages/dataviews/src/view-table.js b/packages/dataviews/src/view-table.js index 083931bb5203e..0be2ee6767ddf 100644 --- a/packages/dataviews/src/view-table.js +++ b/packages/dataviews/src/view-table.js @@ -91,8 +91,8 @@ const HeaderMenu = forwardRef( function HeaderMenu( + { showAddPageModal && ( + + ) } + + } > Date: Wed, 10 Jan 2024 17:57:07 +0900 Subject: [PATCH 33/51] Image Block: Change upload icon label (#57704) --- packages/block-library/src/image/image.js | 2 +- test/e2e/specs/editor/blocks/image.spec.js | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index ea12457c2585d..d8788fde4844f 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -459,7 +459,7 @@ export default function Image( { diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js index adeabc860c834..9080a6dc19402 100644 --- a/test/e2e/specs/editor/blocks/image.spec.js +++ b/test/e2e/specs/editor/blocks/image.spec.js @@ -696,7 +696,9 @@ test.describe( 'Image', () => { await expect( linkDom ).toHaveAttribute( 'href', url ); } ); - test( 'should upload external image', async ( { editor } ) => { + test( 'should upload external image to media library', async ( { + editor, + } ) => { await editor.insertBlock( { name: 'core/image', attributes: { @@ -704,7 +706,7 @@ test.describe( 'Image', () => { }, } ); - await editor.clickBlockToolbarButton( 'Upload external image' ); + await editor.clickBlockToolbarButton( 'Upload image to media library' ); const imageBlock = editor.canvas.locator( 'role=document[name="Block: Image"i]' From 3eb3eca08a90c36603ade6a754c1ab2fc4e6d92a Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Wed, 10 Jan 2024 10:18:54 +0100 Subject: [PATCH 34/51] Use full text instead of abbreviation for min height setting. (#57680) --- .../src/components/global-styles/dimensions-panel.js | 4 ++-- packages/block-editor/src/components/height-control/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/global-styles/dimensions-panel.js b/packages/block-editor/src/components/global-styles/dimensions-panel.js index 47b5bd329725a..47e50aa515e3c 100644 --- a/packages/block-editor/src/components/global-styles/dimensions-panel.js +++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js @@ -603,7 +603,7 @@ export default function DimensionsPanel( { { showMinHeightControl && ( diff --git a/packages/block-editor/src/components/height-control/README.md b/packages/block-editor/src/components/height-control/README.md index 67b52f1d56f9b..9be1741e8cdd8 100644 --- a/packages/block-editor/src/components/height-control/README.md +++ b/packages/block-editor/src/components/height-control/README.md @@ -43,7 +43,7 @@ A callback function that handles the application of the height value. - **Type:** `String` - **Default:** `'Height'` -A label for the height control. This is useful when using the height control for a feature that is controlled in the same way as height, but requires a different label. For example, "Min. height". +A label for the height control. This is useful when using the height control for a feature that is controlled in the same way as height, but requires a different label. For example, "Minimum height". ## Related components From 4c9b8cc09fc57f5d62b740b5eef3c6672f0b116a Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 10 Jan 2024 13:22:33 +0400 Subject: [PATCH 35/51] Editor: Use hooks instead of HoCs in 'PostVisibilityCheck' (#57705) --- .../src/components/post-visibility/check.js | 25 +++++-------- .../components/post-visibility/test/check.js | 37 ++++++++++++------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/packages/editor/src/components/post-visibility/check.js b/packages/editor/src/components/post-visibility/check.js index 4bf9bd03772da..116db0f546de2 100644 --- a/packages/editor/src/components/post-visibility/check.js +++ b/packages/editor/src/components/post-visibility/check.js @@ -1,26 +1,21 @@ /** * WordPress dependencies */ -import { compose } from '@wordpress/compose'; -import { withSelect } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import { store as editorStore } from '../../store'; -export function PostVisibilityCheck( { hasPublishAction, render } ) { - const canEdit = hasPublishAction; +export default function PostVisibilityCheck( { render } ) { + const canEdit = useSelect( ( select ) => { + return ( + select( editorStore ).getCurrentPost()._links?.[ + 'wp:action-publish' + ] ?? false + ); + } ); + return render( { canEdit } ); } - -export default compose( [ - withSelect( ( select ) => { - const { getCurrentPost, getCurrentPostType } = select( editorStore ); - return { - hasPublishAction: - getCurrentPost()._links?.[ 'wp:action-publish' ] ?? false, - postType: getCurrentPostType(), - }; - } ), -] )( PostVisibilityCheck ); diff --git a/packages/editor/src/components/post-visibility/test/check.js b/packages/editor/src/components/post-visibility/test/check.js index 8ec0c2df04ec9..828e876cceb10 100644 --- a/packages/editor/src/components/post-visibility/test/check.js +++ b/packages/editor/src/components/post-visibility/test/check.js @@ -3,32 +3,43 @@ */ import { render, screen } from '@testing-library/react'; +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; + +jest.mock( '@wordpress/data/src/components/use-select', () => jest.fn() ); + /** * Internal dependencies */ -import { PostVisibilityCheck } from '../check'; +import PostVisibilityCheck from '../check'; + +function setupMockSelect( hasPublishAction ) { + useSelect.mockImplementation( ( mapSelect ) => { + return mapSelect( () => ( { + getCurrentPost: () => ( { + _links: { + 'wp:action-publish': hasPublishAction, + }, + } ), + } ) ); + } ); +} describe( 'PostVisibilityCheck', () => { const renderProp = ( { canEdit } ) => ( canEdit ? 'yes' : 'no' ); it( "should not render the edit link if the user doesn't have the right capability", () => { - render( - - ); + setupMockSelect( false ); + render( ); expect( screen.queryByText( 'yes' ) ).not.toBeInTheDocument(); expect( screen.getByText( 'no' ) ).toBeVisible(); } ); it( 'should render if the user has the correct capability', () => { - render( - - ); + setupMockSelect( true ); + render( ); expect( screen.queryByText( 'no' ) ).not.toBeInTheDocument(); expect( screen.getByText( 'yes' ) ).toBeVisible(); } ); From 6ef4629f77ab3da6502508b4c9539a3912300f0e Mon Sep 17 00:00:00 2001 From: Andrea Fercia Date: Wed, 10 Jan 2024 10:35:55 +0100 Subject: [PATCH 36/51] Clean up code editor CSS. (#57519) --- packages/edit-post/src/components/text-editor/style.scss | 6 ------ packages/edit-site/src/components/code-editor/style.scss | 4 ---- 2 files changed, 10 deletions(-) diff --git a/packages/edit-post/src/components/text-editor/style.scss b/packages/edit-post/src/components/text-editor/style.scss index c02e983057e6e..ab248317de1db 100644 --- a/packages/edit-post/src/components/text-editor/style.scss +++ b/packages/edit-post/src/components/text-editor/style.scss @@ -39,10 +39,8 @@ margin-right: auto; @include break-large() { - padding: $grid-unit-20 $grid-unit-30 #{ $grid-unit-60 * 2 } $grid-unit-30; padding: 0 $grid-unit-30 $grid-unit-30 $grid-unit-30; } - } // Exit code editor toolbar. @@ -70,8 +68,4 @@ font-size: $default-font-size; color: $gray-900; } - - .components-button svg { - order: 1; - } } diff --git a/packages/edit-site/src/components/code-editor/style.scss b/packages/edit-site/src/components/code-editor/style.scss index 0e79575c49f67..17431de27b896 100644 --- a/packages/edit-site/src/components/code-editor/style.scss +++ b/packages/edit-site/src/components/code-editor/style.scss @@ -41,10 +41,6 @@ font-size: $default-font-size; color: $gray-900; } - - .components-button svg { - order: 1; - } } } From 73a4716f429b5dce0190638049f5bd30f0b242f6 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Jan 2024 12:47:03 +0100 Subject: [PATCH 37/51] Scripts: Fix webpack not setting environment.module true (#57714) Webpack (via wp-scripts) may refuse to output external modules unless we set `output.environment.modules = true`: > The target environment doesn't support EcmaScriptModule syntax so it's not possible to use external type 'module' > while analyzing module external module "@wordpress/interactivity" for concatenation --- packages/scripts/config/webpack.config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/scripts/config/webpack.config.js b/packages/scripts/config/webpack.config.js index 3919558c2f05c..57bd258d32539 100644 --- a/packages/scripts/config/webpack.config.js +++ b/packages/scripts/config/webpack.config.js @@ -403,6 +403,10 @@ if ( hasExperimentalModulesFlag ) { ...baseConfig.output, module: true, chunkFormat: 'module', + environment: { + ...baseConfig.output.environment, + module: true, + }, library: { ...baseConfig.output.library, type: 'module', From b3e534e636cc286ca79a796d3a47164ff362302b Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 10 Jan 2024 13:35:36 +0100 Subject: [PATCH 38/51] Add tests to cover enabling autoplay and loop setting in Audio block (#57715) --- .../test/__snapshots__/edit.native.js.snap | 12 ++++++++ .../src/audio/test/edit.native.js | 29 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/packages/block-library/src/audio/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/audio/test/__snapshots__/edit.native.js.snap index dca3f782efc67..4cf28f7063ad3 100644 --- a/packages/block-library/src/audio/test/__snapshots__/edit.native.js.snap +++ b/packages/block-library/src/audio/test/__snapshots__/edit.native.js.snap @@ -532,3 +532,15 @@ exports[`Audio block renders placeholder without crashing 1`] = ` `; + +exports[`Audio block should enable autoplay setting 1`] = ` +" +
+" +`; + +exports[`Audio block should enable loop setting 1`] = ` +" +
+" +`; diff --git a/packages/block-library/src/audio/test/edit.native.js b/packages/block-library/src/audio/test/edit.native.js index c191fd2fff798..7296d595d7aaa 100644 --- a/packages/block-library/src/audio/test/edit.native.js +++ b/packages/block-library/src/audio/test/edit.native.js @@ -5,7 +5,10 @@ import { addBlock, dismissModal, fireEvent, + getBlock, + getEditorHtml, initializeEditor, + openBlockSettings, render, screen, setupCoreBlocks, @@ -31,6 +34,10 @@ jest.unmock( '@wordpress/react-native-aztec' ); const MEDIA_UPLOAD_STATE_FAILED = 3; +const AUDIO_BLOCK = ` +
+`; + let uploadCallBack; subscribeMediaUpload.mockImplementation( ( callback ) => { uploadCallBack = callback; @@ -100,4 +107,26 @@ describe( 'Audio block', () => { screen.getByText( 'Invalid URL. Audio file not found.' ) ).toBeVisible(); } ); + + it( 'should enable autoplay setting', async () => { + await initializeEditor( { initialHtml: AUDIO_BLOCK } ); + + const audioBlock = getBlock( screen, 'Audio' ); + fireEvent.press( audioBlock ); + await openBlockSettings( screen ); + + fireEvent.press( screen.getByText( 'Autoplay' ) ); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'should enable loop setting', async () => { + await initializeEditor( { initialHtml: AUDIO_BLOCK } ); + + const audioBlock = getBlock( screen, 'Audio' ); + fireEvent.press( audioBlock ); + await openBlockSettings( screen ); + + fireEvent.press( screen.getByText( 'Loop' ) ); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); From db067e16df101128fe7588e483112c7030dfa83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= <583546+oandregal@users.noreply.github.com> Date: Wed, 10 Jan 2024 13:51:21 +0100 Subject: [PATCH 39/51] DataViews: add footer to Pages sidebar (#57690) --- .../index.js | 77 +++++++++++++++++++ .../edit-site/src/components/sidebar/index.js | 9 +-- 2 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 packages/edit-site/src/components/sidebar-navigation-screen-pages-dataviews/index.js diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pages-dataviews/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-pages-dataviews/index.js new file mode 100644 index 0000000000000..171d59c108e9b --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-pages-dataviews/index.js @@ -0,0 +1,77 @@ +/** + * WordPress dependencies + */ +import { + __experimentalTruncate as Truncate, + __experimentalVStack as VStack, +} from '@wordpress/components'; +import { layout } from '@wordpress/icons'; +import { useMemo } from '@wordpress/element'; +import { useEntityRecords } from '@wordpress/core-data'; +import { decodeEntities } from '@wordpress/html-entities'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { useLink } from '../routes/link'; +import { TEMPLATE_POST_TYPE } from '../../utils/constants'; +import SidebarNavigationItem from '../sidebar-navigation-item'; +import SidebarNavigationScreen from '../sidebar-navigation-screen'; +import DataViewsSidebarContent from '../sidebar-dataviews'; + +const PageItem = ( { postType = 'page', postId, ...props } ) => { + const linkInfo = useLink( + { + postType, + postId, + }, + { + backPath: '/page', + } + ); + return ; +}; + +export default function SidebarNavigationScreenPagesDataViews() { + const { records: templateRecords } = useEntityRecords( + 'postType', + TEMPLATE_POST_TYPE, + { + per_page: -1, + } + ); + const templates = useMemo( + () => + templateRecords?.filter( ( { slug } ) => + [ '404', 'search' ].includes( slug ) + ), + [ templateRecords ] + ); + + return ( + } + footer={ + + { templates?.map( ( item ) => ( + + + { decodeEntities( + item.title?.rendered || __( '(no title)' ) + ) } + + + ) ) } + + } + /> + ); +} diff --git a/packages/edit-site/src/components/sidebar/index.js b/packages/edit-site/src/components/sidebar/index.js index 0f986d486fbb8..73c6aea7e328c 100644 --- a/packages/edit-site/src/components/sidebar/index.js +++ b/packages/edit-site/src/components/sidebar/index.js @@ -7,7 +7,6 @@ import classNames from 'classnames'; * WordPress dependencies */ import { memo, useRef } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; import { __experimentalNavigatorProvider as NavigatorProvider, __experimentalNavigatorScreen as NavigatorScreen, @@ -32,9 +31,8 @@ import SidebarNavigationScreenTemplatesBrowse from '../sidebar-navigation-screen import SaveHub from '../save-hub'; import { unlock } from '../../lock-unlock'; import SidebarNavigationScreenPages from '../sidebar-navigation-screen-pages'; +import SidebarNavigationScreenPagesDataViews from '../sidebar-navigation-screen-pages-dataviews'; import SidebarNavigationScreenPage from '../sidebar-navigation-screen-page'; -import SidebarNavigationScreen from '../sidebar-navigation-screen'; -import DataViewsSidebarContent from '../sidebar-dataviews'; const { useLocation } = unlock( routerPrivateApis ); @@ -69,10 +67,7 @@ function SidebarScreens() { { window?.__experimentalAdminViews ? ( - } - /> + ) : ( ) } From 08f236d0319f503c1c784dc8050df058cbccdea9 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Wed, 10 Jan 2024 15:13:22 +0100 Subject: [PATCH 40/51] Interactive template: Use viewModule (#57712) Use viewModule in the create-block interactivity template. - Add `viewModule` support to `@wordpress/create-block`. - Set the `--experimental-modules` option when building the templated block. - The plugin php template is updated to register with block metadata. --------- Co-authored-by: Luis Herranz --- .../CHANGELOG.md | 14 +++++++++----- .../create-block-interactive-template/README.md | 2 ++ .../block-templates/render.php.mustache | 5 ----- .../create-block-interactive-template/index.js | 6 ++++++ .../plugin-templates/$slug.php.mustache | 11 +---------- packages/create-block/CHANGELOG.md | 4 ++++ packages/create-block/lib/init-block.js | 2 ++ packages/create-block/lib/scaffold.js | 2 ++ packages/interactivity/docs/1-getting-started.md | 16 ++-------------- 9 files changed, 28 insertions(+), 34 deletions(-) diff --git a/packages/create-block-interactive-template/CHANGELOG.md b/packages/create-block-interactive-template/CHANGELOG.md index 47a8aec6c92a3..159c65e9ada19 100644 --- a/packages/create-block-interactive-template/CHANGELOG.md +++ b/packages/create-block-interactive-template/CHANGELOG.md @@ -2,20 +2,24 @@ ## Unreleased +### Enhancement + +- Update the template to use `viewModule` in block.json ([#57712](https://github.com/WordPress/gutenberg/pull/57712)). + ## 1.11.0 (2023-12-13) -- Add all files to the generated plugin zip. [#56943](https://github.com/WordPress/gutenberg/pull/56943) -- Prevent crash when Gutenberg plugin is not installed. [#56941](https://github.com/WordPress/gutenberg/pull/56941) +- Add all files to the generated plugin zip ([#56943](https://github.com/WordPress/gutenberg/pull/56943)). +- Prevent crash when Gutenberg plugin is not installed ([#56941](https://github.com/WordPress/gutenberg/pull/56941)). ## 1.10.1 (2023-12-07) -- Update template to use modules instead of scripts. [#56694](https://github.com/WordPress/gutenberg/pull/56694) +- Update template to use modules instead of scripts ([#56694](https://github.com/WordPress/gutenberg/pull/56694)). ## 1.10.0 (2023-11-29) ### Enhancement -- Update `view.js` and `render.php` templates to the new `store()` API. [#56613](https://github.com/WordPress/gutenberg/pull/56613) +- Update `view.js` and `render.php` templates to the new `store()` API ([#56613](https://github.com/WordPress/gutenberg/pull/56613)). ## 1.9.0 (2023-11-16) @@ -35,4 +39,4 @@ ### Enhancement -- Moves the `example` property into block.json by leveraging changes to create-block to now support `example`. [#52801](https://github.com/WordPress/gutenberg/pull/52801) +- Moves the `example` property into block.json by leveraging changes to create-block to now support `example` ([#52801](https://github.com/WordPress/gutenberg/pull/52801)). diff --git a/packages/create-block-interactive-template/README.md b/packages/create-block-interactive-template/README.md index cc0530c063054..adf3cab6594cc 100644 --- a/packages/create-block-interactive-template/README.md +++ b/packages/create-block-interactive-template/README.md @@ -10,6 +10,8 @@ This block template can be used by running the following command: npx @wordpress/create-block --template @wordpress/create-block-interactive-template ``` +It requires Gutenberg 17.5 or higher. + ## Contributing to this package This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects. diff --git a/packages/create-block-interactive-template/block-templates/render.php.mustache b/packages/create-block-interactive-template/block-templates/render.php.mustache index 0f6883a936240..960da619f790a 100644 --- a/packages/create-block-interactive-template/block-templates/render.php.mustache +++ b/packages/create-block-interactive-template/block-templates/render.php.mustache @@ -13,11 +13,6 @@ // Generate unique id for aria-controls. $unique_id = wp_unique_id( 'p-' ); - -// Enqueue the view file. -if (function_exists('gutenberg_enqueue_module')) { - gutenberg_enqueue_module( '{{namespace}}-view' ); -} ?>
!! value ) diff --git a/packages/create-block/lib/scaffold.js b/packages/create-block/lib/scaffold.js index 49d3cbf794777..bd9ba0396b75e 100644 --- a/packages/create-block/lib/scaffold.js +++ b/packages/create-block/lib/scaffold.js @@ -44,6 +44,7 @@ module.exports = async ( editorStyle, style, render, + viewModule, viewScript, variantVars, customPackageJSON, @@ -84,6 +85,7 @@ module.exports = async ( editorStyle, style, render, + viewModule, viewScript, variantVars, customPackageJSON, diff --git a/packages/interactivity/docs/1-getting-started.md b/packages/interactivity/docs/1-getting-started.md index 85af202180735..660671a8b10cd 100644 --- a/packages/interactivity/docs/1-getting-started.md +++ b/packages/interactivity/docs/1-getting-started.md @@ -26,18 +26,6 @@ We can scaffold a WordPress plugin that registers an interactive block (using th npx @wordpress/create-block@latest my-first-interactive-block --template @wordpress/create-block-interactive-template ``` -> **Note** -> The Interactivity API recently switched from [using modules instead of scripts in the frontend](https://github.com/WordPress/gutenberg/pull/56143). Therefore, in order to test this scaffolded block, you will need to add the following line to the `package.json` file of the generated plugin: - -```json -"files": [ - "src/view.js" -] -``` -> This should be updated in the [scripts package](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/) soon. - - - #### 2. Generate the build When the plugin folder is generated, we should launch the build process to get the final version of the interactive block that can be used from WordPress. @@ -61,7 +49,7 @@ At this point you should be able to insert the "My First Interactive Block" bloc ## Requirements of the Interactivity API -To start working with the Interactivity API you'll need to have a [proper WordPress development environment for blocks](https://developer.wordpress.org/block-editor/getting-started/devenv/) and some specific code in your block, which should include: +To start working with the Interactivity API you'll need to have a [proper WordPress development environment for blocks](https://developer.wordpress.org/block-editor/getting-started/devenv/) and some specific code in your block, which should include: #### A local WordPress installation @@ -71,7 +59,7 @@ To get quickly started, [`wp-now`](https://www.npmjs.com/package/@wp-now/wp-now) #### Latest vesion of Gutenberg -The Interactivity API is currently only available as an experimental feature from Gutenberg 17.2, so you'll need to have Gutenberg 17.2 or higher version installed and activated in your WordPress installation. +The Interactivity API is currently only available as an experimental feature from Gutenberg, so you'll need to have Gutenberg 17.5 or higher version installed and activated in your WordPress installation. #### Node.js From ba367dcc86de157f74eebf419f167381b5d797b2 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 10 Jan 2024 15:28:52 +0100 Subject: [PATCH 41/51] Update @ariakit/react to v0.3.12 and @ariakit/test to v0.3.7 (#57547) * Update @ariakit/react to v0.3.12 and @ariakit/test to v0.3.7 * CHANGELOG * Use @ariakit/test for AlignmentMatrixControl unit tests * Use @ariakit/text for ToggleGroupControl unit tests * Refactor 'hoverOutside' method * Improve tooltip-related tests in `ToggleGroupControl` --- package-lock.json | 152 ++++++++---------- package.json | 2 +- packages/components/CHANGELOG.md | 1 + packages/components/package.json | 2 +- .../alignment-matrix-control/test/index.tsx | 26 ++- .../src/toggle-group-control/test/index.tsx | 103 ++++++------ 6 files changed, 140 insertions(+), 146 deletions(-) diff --git a/package-lock.json b/package-lock.json index d2c4a65342a35..02de0a60ff4c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,7 +86,7 @@ "devDependencies": { "@actions/core": "1.9.1", "@actions/github": "5.0.0", - "@ariakit/test": "^0.3.5", + "@ariakit/test": "^0.3.7", "@babel/core": "7.16.0", "@babel/plugin-proposal-export-namespace-from": "7.18.9", "@babel/plugin-syntax-jsx": "7.16.0", @@ -1628,13 +1628,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@ariakit/core": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.3.10.tgz", + "integrity": "sha512-AcN+GSoVXuUOzKx5d3xPL3YsEHevh4PIO6QIt/mg/nRX1XQ6cvxQEiAjO/BJQm+/MVl7/VbuGBoTFjr0tPU6NQ==" + }, + "node_modules/@ariakit/react": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.3.12.tgz", + "integrity": "sha512-HxKMZZhWSkwwS/Sh9OdWyuNKQ2tjDAIQIy2KVI7IRa8ZQ6ze/4g3YLUHbfCxO7oDupXHfXaeZ4hWx8lP7l1U/g==", + "dependencies": { + "@ariakit/react-core": "0.3.12" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ariakit" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@ariakit/react-core": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.3.12.tgz", + "integrity": "sha512-w6P1A7TYb1fKUe9QbwaoTOWofl13g7TEuXdV4JyefJCQL1e9HQdEw9UL67I8aXRo8/cFHH94/z0N37t8hw5Ogg==", + "dependencies": { + "@ariakit/core": "0.3.10", + "@floating-ui/dom": "^1.0.0", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, "node_modules/@ariakit/test": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@ariakit/test/-/test-0.3.5.tgz", - "integrity": "sha512-7UCQBnJZ88JptkEnAXT7iSgtxEZiFwqdkKtxLCXDssTOJNatbFsnq0Jow324y41jGfAE2n4Lf5qY2FsZUPf9XQ==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@ariakit/test/-/test-0.3.7.tgz", + "integrity": "sha512-rOa9pJA0ZfPPSI4SkDX41CsBcvxs6BmxgzFEElZWZo/uBBqtnr8ZL4oe5HySeZKEAHRH86XDqfxFISkhV76m5g==", "dev": true, "dependencies": { - "@ariakit/core": "0.3.8", + "@ariakit/core": "0.3.10", "@testing-library/dom": "^8.0.0 || ^9.0.0" }, "peerDependencies": { @@ -1650,12 +1685,6 @@ } } }, - "node_modules/@ariakit/test/node_modules/@ariakit/core": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.3.8.tgz", - "integrity": "sha512-LlSCwbyyozMX4ZEobpYGcv1LFqOdBTdTYPZw3lAVgLcFSNivsazi3NkKM9qNWNIu00MS+xTa0+RuIcuWAjlB2Q==", - "dev": true - }, "node_modules/@aw-web-design/x-default-browser": { "version": "1.4.126", "resolved": "https://registry.npmjs.org/@aw-web-design/x-default-browser/-/x-default-browser-1.4.126.tgz", @@ -54244,7 +54273,7 @@ "version": "25.14.0", "license": "GPL-2.0-or-later", "dependencies": { - "@ariakit/react": "^0.3.10", + "@ariakit/react": "^0.3.12", "@babel/runtime": "^7.16.0", "@emotion/cache": "^11.7.1", "@emotion/css": "^11.7.1", @@ -54303,41 +54332,6 @@ "react-dom": "^18.0.0" } }, - "packages/components/node_modules/@ariakit/core": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.3.8.tgz", - "integrity": "sha512-LlSCwbyyozMX4ZEobpYGcv1LFqOdBTdTYPZw3lAVgLcFSNivsazi3NkKM9qNWNIu00MS+xTa0+RuIcuWAjlB2Q==" - }, - "packages/components/node_modules/@ariakit/react": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.3.10.tgz", - "integrity": "sha512-XRY69IOm8Oy+HSPoaspcVLAhLo3ToLhhJKSLK1voTAZtSzu5kUeUf4nUPxTzYFsvirKORZgOLAeNwuo1gPr61g==", - "dependencies": { - "@ariakit/react-core": "0.3.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ariakit" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - } - }, - "packages/components/node_modules/@ariakit/react-core": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.3.10.tgz", - "integrity": "sha512-CzSffcNlOyS2xuy21UB6fgJXi5LriJ9JrTSJzcgJmE+P9/WfQlplJC3L75d8O2yKgaGPeFnQ0hhDA6ItsI98eQ==", - "dependencies": { - "@ariakit/core": "0.3.8", - "@floating-ui/dom": "^1.0.0", - "use-sync-external-store": "^1.2.0" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - } - }, "packages/components/node_modules/@floating-ui/react-dom": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz", @@ -57392,22 +57386,37 @@ } } }, + "@ariakit/core": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.3.10.tgz", + "integrity": "sha512-AcN+GSoVXuUOzKx5d3xPL3YsEHevh4PIO6QIt/mg/nRX1XQ6cvxQEiAjO/BJQm+/MVl7/VbuGBoTFjr0tPU6NQ==" + }, + "@ariakit/react": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.3.12.tgz", + "integrity": "sha512-HxKMZZhWSkwwS/Sh9OdWyuNKQ2tjDAIQIy2KVI7IRa8ZQ6ze/4g3YLUHbfCxO7oDupXHfXaeZ4hWx8lP7l1U/g==", + "requires": { + "@ariakit/react-core": "0.3.12" + } + }, + "@ariakit/react-core": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.3.12.tgz", + "integrity": "sha512-w6P1A7TYb1fKUe9QbwaoTOWofl13g7TEuXdV4JyefJCQL1e9HQdEw9UL67I8aXRo8/cFHH94/z0N37t8hw5Ogg==", + "requires": { + "@ariakit/core": "0.3.10", + "@floating-ui/dom": "^1.0.0", + "use-sync-external-store": "^1.2.0" + } + }, "@ariakit/test": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@ariakit/test/-/test-0.3.5.tgz", - "integrity": "sha512-7UCQBnJZ88JptkEnAXT7iSgtxEZiFwqdkKtxLCXDssTOJNatbFsnq0Jow324y41jGfAE2n4Lf5qY2FsZUPf9XQ==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@ariakit/test/-/test-0.3.7.tgz", + "integrity": "sha512-rOa9pJA0ZfPPSI4SkDX41CsBcvxs6BmxgzFEElZWZo/uBBqtnr8ZL4oe5HySeZKEAHRH86XDqfxFISkhV76m5g==", "dev": true, "requires": { - "@ariakit/core": "0.3.8", + "@ariakit/core": "0.3.10", "@testing-library/dom": "^8.0.0 || ^9.0.0" - }, - "dependencies": { - "@ariakit/core": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.3.8.tgz", - "integrity": "sha512-LlSCwbyyozMX4ZEobpYGcv1LFqOdBTdTYPZw3lAVgLcFSNivsazi3NkKM9qNWNIu00MS+xTa0+RuIcuWAjlB2Q==", - "dev": true - } } }, "@aw-web-design/x-default-browser": { @@ -69363,7 +69372,7 @@ "@wordpress/components": { "version": "file:packages/components", "requires": { - "@ariakit/react": "^0.3.10", + "@ariakit/react": "^0.3.12", "@babel/runtime": "^7.16.0", "@emotion/cache": "^11.7.1", "@emotion/css": "^11.7.1", @@ -69415,29 +69424,6 @@ "valtio": "1.7.0" }, "dependencies": { - "@ariakit/core": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.3.8.tgz", - "integrity": "sha512-LlSCwbyyozMX4ZEobpYGcv1LFqOdBTdTYPZw3lAVgLcFSNivsazi3NkKM9qNWNIu00MS+xTa0+RuIcuWAjlB2Q==" - }, - "@ariakit/react": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.3.10.tgz", - "integrity": "sha512-XRY69IOm8Oy+HSPoaspcVLAhLo3ToLhhJKSLK1voTAZtSzu5kUeUf4nUPxTzYFsvirKORZgOLAeNwuo1gPr61g==", - "requires": { - "@ariakit/react-core": "0.3.10" - } - }, - "@ariakit/react-core": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.3.10.tgz", - "integrity": "sha512-CzSffcNlOyS2xuy21UB6fgJXi5LriJ9JrTSJzcgJmE+P9/WfQlplJC3L75d8O2yKgaGPeFnQ0hhDA6ItsI98eQ==", - "requires": { - "@ariakit/core": "0.3.8", - "@floating-ui/dom": "^1.0.0", - "use-sync-external-store": "^1.2.0" - } - }, "@floating-ui/react-dom": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz", diff --git a/package.json b/package.json index e98ad7cb1587f..f2f239762de97 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "devDependencies": { "@actions/core": "1.9.1", "@actions/github": "5.0.0", - "@ariakit/test": "^0.3.5", + "@ariakit/test": "^0.3.7", "@babel/core": "7.16.0", "@babel/plugin-proposal-export-namespace-from": "7.18.9", "@babel/plugin-syntax-jsx": "7.16.0", diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 8805736c2e440..c7c1a515a64ce 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -24,6 +24,7 @@ ### Enhancements - Update `ariakit` to version `0.3.10` ([#57325](https://github.com/WordPress/gutenberg/pull/57325)). +- Update `@ariakit/react` to version `0.3.12` and @ariakit/test to version `0.3.7` ([#57547](https://github.com/WordPress/gutenberg/pull/57547)). - `DropdownMenuV2`: do not collapse suffix width ([#57238](https://github.com/WordPress/gutenberg/pull/57238)). - `DateTimePicker`: Adjustment of the dot position on DayButton and expansion of the button area. ([#55502](https://github.com/WordPress/gutenberg/pull/55502)). - `Modal`: Improve application of body class names ([#55430](https://github.com/WordPress/gutenberg/pull/55430)). diff --git a/packages/components/package.json b/packages/components/package.json index 885c1e455fea4..cd440998b9323 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -30,7 +30,7 @@ ], "types": "build-types", "dependencies": { - "@ariakit/react": "^0.3.10", + "@ariakit/react": "^0.3.12", "@babel/runtime": "^7.16.0", "@emotion/cache": "^11.7.1", "@emotion/css": "^11.7.1", diff --git a/packages/components/src/alignment-matrix-control/test/index.tsx b/packages/components/src/alignment-matrix-control/test/index.tsx index 6836bc7e45f95..a820b69b26c8f 100644 --- a/packages/components/src/alignment-matrix-control/test/index.tsx +++ b/packages/components/src/alignment-matrix-control/test/index.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { render, screen, waitFor, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import { press, click } from '@ariakit/test'; /** * Internal dependencies @@ -37,11 +37,9 @@ describe( 'AlignmentMatrixControl', () => { } ); it( 'should be centered by default', async () => { - const user = userEvent.setup(); - await renderAndInitCompositeStore( ); - await user.tab(); + await press.Tab(); expect( getCell( 'center center' ) ).toHaveFocus(); } ); @@ -60,7 +58,6 @@ describe( 'AlignmentMatrixControl', () => { 'bottom center', 'bottom right', ] )( '%s', async ( alignment ) => { - const user = userEvent.setup(); const spy = jest.fn(); await renderAndInitCompositeStore( @@ -72,14 +69,13 @@ describe( 'AlignmentMatrixControl', () => { const cell = getCell( alignment ); - await user.click( cell ); + await click( cell ); expect( cell ).toHaveFocus(); expect( spy ).toHaveBeenCalledWith( alignment ); } ); it( 'unless already focused', async () => { - const user = userEvent.setup(); const spy = jest.fn(); await renderAndInitCompositeStore( @@ -91,7 +87,7 @@ describe( 'AlignmentMatrixControl', () => { const cell = getCell( 'center center' ); - await user.click( cell ); + await click( cell ); expect( cell ).toHaveFocus(); expect( spy ).not.toHaveBeenCalled(); @@ -106,16 +102,15 @@ describe( 'AlignmentMatrixControl', () => { [ 'ArrowLeft', 'center left' ], [ 'ArrowDown', 'bottom center' ], [ 'ArrowRight', 'center right' ], - ] )( '%s', async ( keyRef, cellRef ) => { - const user = userEvent.setup(); + ] as const )( '%s', async ( keyRef, cellRef ) => { const spy = jest.fn(); await renderAndInitCompositeStore( ); - await user.tab(); - await user.keyboard( `[${ keyRef }]` ); + await press.Tab(); + await press[ keyRef ](); expect( getCell( cellRef ) ).toHaveFocus(); expect( spy ).toHaveBeenCalledWith( cellRef ); @@ -128,8 +123,7 @@ describe( 'AlignmentMatrixControl', () => { [ 'ArrowLeft', 'top left' ], [ 'ArrowDown', 'bottom right' ], [ 'ArrowRight', 'bottom right' ], - ] )( '%s', async ( keyRef, cellRef ) => { - const user = userEvent.setup(); + ] as const )( '%s', async ( keyRef, cellRef ) => { const spy = jest.fn(); await renderAndInitCompositeStore( @@ -137,8 +131,8 @@ describe( 'AlignmentMatrixControl', () => { ); const cell = getCell( cellRef ); - await user.click( cell ); - await user.keyboard( `[${ keyRef }]` ); + await click( cell ); + await press[ keyRef ](); expect( cell ).toHaveFocus(); expect( spy ).toHaveBeenCalledWith( cellRef ); diff --git a/packages/components/src/toggle-group-control/test/index.tsx b/packages/components/src/toggle-group-control/test/index.tsx index b54b5764d4e0f..99a2dd8a00421 100644 --- a/packages/components/src/toggle-group-control/test/index.tsx +++ b/packages/components/src/toggle-group-control/test/index.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import { press, click, hover, sleep } from '@ariakit/test'; /** * WordPress dependencies @@ -19,8 +19,13 @@ import { ToggleGroupControlOption, ToggleGroupControlOptionIcon, } from '../index'; +import { TOOLTIP_DELAY } from '../../tooltip'; import type { ToggleGroupControlProps } from '../types'; -import cleanupTooltip from '../../tooltip/test/utils'; + +const hoverOutside = async () => { + await hover( document.body ); + await hover( document.body, { clientX: 10, clientY: 10 } ); +}; const ControlledToggleGroupControl = ( { value: valueProp, @@ -113,7 +118,6 @@ describe.each( [ } ); } ); it( 'should call onChange with proper value', async () => { - const user = userEvent.setup(); const mockOnChange = jest.fn(); render( @@ -126,13 +130,12 @@ describe.each( [ ); - await user.click( screen.getByRole( 'radio', { name: 'R' } ) ); + await click( screen.getByRole( 'radio', { name: 'R' } ) ); expect( mockOnChange ).toHaveBeenCalledWith( 'rigas' ); } ); it( 'should render tooltip where `showTooltip` === `true`', async () => { - const user = userEvent.setup(); render( { optionsWithTooltip } @@ -143,19 +146,26 @@ describe.each( [ 'Click for Delicious Gnocchi' ); - await user.hover( firstRadio ); + await hover( firstRadio ); - const tooltip = await screen.findByText( - 'Click for Delicious Gnocchi' - ); + const tooltip = await screen.findByRole( 'tooltip', { + name: 'Click for Delicious Gnocchi', + } ); await waitFor( () => expect( tooltip ).toBeVisible() ); - await cleanupTooltip( user ); + // hover outside of radio + await hoverOutside(); + + // Tooltip should hide + expect( + screen.queryByRole( 'tooltip', { + name: 'Click for Delicious Gnocchi', + } ) + ).not.toBeInTheDocument(); } ); it( 'should not render tooltip', async () => { - const user = userEvent.setup(); render( { optionsWithTooltip } @@ -166,19 +176,24 @@ describe.each( [ 'Click for Sumptuous Caponata' ); - await user.hover( secondRadio ); + await hover( secondRadio ); - await waitFor( () => - expect( - screen.queryByText( 'Click for Sumptuous Caponata' ) - ).not.toBeInTheDocument() - ); + // Tooltip shouldn't show + expect( + screen.queryByText( 'Click for Sumptuous Caponata' ) + ).not.toBeInTheDocument(); + + // Advance time by default delay + await sleep( TOOLTIP_DELAY ); + + // Tooltip shouldn't show. + expect( + screen.queryByText( 'Click for Sumptuous Caponata' ) + ).not.toBeInTheDocument(); } ); if ( mode === 'controlled' ) { it( 'should reset values correctly', async () => { - const user = userEvent.setup(); - render( { options } @@ -188,25 +203,23 @@ describe.each( [ const rigasOption = screen.getByRole( 'radio', { name: 'R' } ); const jackOption = screen.getByRole( 'radio', { name: 'J' } ); - await user.click( rigasOption ); + await click( rigasOption ); expect( jackOption ).not.toBeChecked(); expect( rigasOption ).toBeChecked(); - await user.keyboard( '[ArrowRight]' ); + await press.ArrowRight(); expect( rigasOption ).not.toBeChecked(); expect( jackOption ).toBeChecked(); - await user.click( screen.getByRole( 'button', { name: 'Reset' } ) ); + await click( screen.getByRole( 'button', { name: 'Reset' } ) ); expect( rigasOption ).not.toBeChecked(); expect( jackOption ).not.toBeChecked(); } ); it( 'should update correctly when triggered by external updates', async () => { - const user = userEvent.setup(); - render( { it( 'should not be deselectable', async () => { const mockOnChange = jest.fn(); - const user = userEvent.setup(); render( { - const user = userEvent.setup(); - render( - - { options } - + <> + + { options } + + + ); const rigas = screen.getByRole( 'radio', { name: 'R', } ); - await user.tab(); + await press.Tab(); expect( rigas ).toHaveFocus(); - await user.tab(); + await press.Tab(); + // When in controlled mode, there is an additional "Reset" button. const expectedFocusTarget = mode === 'uncontrolled' - ? rigas.ownerDocument.body + ? screen.getByRole( 'button', { + name: 'After ToggleGroupControl', + } ) : screen.getByRole( 'button', { name: 'Reset' } ); expect( expectedFocusTarget ).toHaveFocus(); @@ -301,7 +317,6 @@ describe.each( [ describe( 'isDeselectable = true', () => { it( 'should be deselectable', async () => { const mockOnChange = jest.fn(); - const user = userEvent.setup(); render( ); - await user.click( + await click( screen.getByRole( 'button', { name: 'R', pressed: true, @@ -323,7 +338,7 @@ describe.each( [ expect( mockOnChange ).toHaveBeenCalledTimes( 1 ); expect( mockOnChange ).toHaveBeenLastCalledWith( undefined ); - await user.click( + await click( screen.getByRole( 'button', { name: 'R', pressed: false, @@ -334,15 +349,13 @@ describe.each( [ } ); it( 'should tab to the next option button', async () => { - const user = userEvent.setup(); - render( { options } ); - await user.tab(); + await press.Tab(); expect( screen.getByRole( 'button', { name: 'R', @@ -350,7 +363,7 @@ describe.each( [ } ) ).toHaveFocus(); - await user.tab(); + await press.Tab(); expect( screen.getByRole( 'button', { name: 'J', @@ -359,7 +372,7 @@ describe.each( [ ).toHaveFocus(); // Focus should not move with arrow keys - await user.keyboard( '{ArrowLeft}' ); + await press.ArrowLeft(); expect( screen.getByRole( 'button', { name: 'J', From 6693377d080d8c581f279bfd7521f5b2bcf02149 Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 10 Jan 2024 14:34:24 +0000 Subject: [PATCH 42/51] Bump plugin version to 17.5.0-rc.1 --- gutenberg.php | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gutenberg.php b/gutenberg.php index 35e416006bea5..9559f838608da 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the block editor, site editor, and other future WordPress core functionality. * Requires at least: 6.3 * Requires PHP: 7.0 - * Version: 17.4.1 + * Version: 17.5.0-rc.1 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/package-lock.json b/package-lock.json index 02de0a60ff4c9..96a14ec8eeb50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gutenberg", - "version": "17.4.1", + "version": "17.5.0-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gutenberg", - "version": "17.4.1", + "version": "17.5.0-rc.1", "hasInstallScript": true, "license": "GPL-2.0-or-later", "dependencies": { diff --git a/package.json b/package.json index f2f239762de97..684f35d408d3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "17.4.1", + "version": "17.5.0-rc.1", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", From e93f250746b14acaed14190cdb552960a6c490ce Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Wed, 10 Jan 2024 15:49:08 +0100 Subject: [PATCH 43/51] [Mobile] - Fix missing custom gradient indicator in the color palette (#57605) * Mobile - Fix missing Custom indicator for custom gradients * Pass enableCustomColor in Cover block as true * Fix typo * Update Changelog * Fix condition * Update test to use the openBlockSettings helper * Simplify condition by adding optional chaining * Use flatMap --- .../test/__snapshots__/edit.native.js.snap | 6 +++ .../src/buttons/test/edit.native.js | 49 +++++++++++++++++++ .../block-library/src/cover/edit.native.js | 1 + .../src/color-palette/index.native.js | 25 +++++++--- .../color-settings/palette.screen.native.js | 12 +++-- packages/react-native-editor/CHANGELOG.md | 1 + 6 files changed, 82 insertions(+), 12 deletions(-) diff --git a/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap index 1a55c807225d9..f04eacee4b91c 100644 --- a/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap +++ b/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap @@ -6,6 +6,12 @@ exports[`Buttons block color customization sets a background color 1`] = ` " `; +exports[`Buttons block color customization sets a custom gradient background color 1`] = ` +" +
+" +`; + exports[`Buttons block color customization sets a gradient background color 1`] = ` "
diff --git a/packages/block-library/src/buttons/test/edit.native.js b/packages/block-library/src/buttons/test/edit.native.js index f393a31c7330a..af2ffe762e6a3 100644 --- a/packages/block-library/src/buttons/test/edit.native.js +++ b/packages/block-library/src/buttons/test/edit.native.js @@ -10,6 +10,7 @@ import { initializeEditor, triggerBlockListLayout, typeInRichText, + openBlockSettings, waitFor, } from 'test/helpers'; @@ -391,5 +392,53 @@ describe( 'Buttons block', () => { // Assert expect( getEditorHtml() ).toMatchSnapshot(); } ); + + it( 'sets a custom gradient background color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Buttons' ); + + // Act + const buttonsBlock = getBlock( screen, 'Buttons' ); + fireEvent.press( buttonsBlock ); + + // Trigger onLayout for the list + await triggerBlockListLayout( buttonsBlock ); + + const buttonBlock = await getBlock( screen, 'Button' ); + fireEvent.press( buttonBlock ); + + // Open Block Settings. + await openBlockSettings( screen ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); + + // Tap on the gradient segment + fireEvent.press( screen.getByLabelText( 'Gradient' ) ); + + // Tap one gradient color + fireEvent.press( + screen.getByLabelText( 'Light green cyan to vivid green cyan' ) + ); + + // Tap on Customize Gradient + fireEvent.press( screen.getByLabelText( /Customize Gradient/ ) ); + + // Change the current angle + fireEvent.press( screen.getByText( '135', { hidden: true } ) ); + const angleTextInput = screen.getByDisplayValue( '135', { + hidden: true, + } ); + fireEvent.changeText( angleTextInput, '200' ); + + // Go back to the settings list. + fireEvent.press( await screen.findByLabelText( 'Go back' ) ); + + // Assert + const customButton = await screen.findByText( 'CUSTOM' ); + expect( customButton ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); } ); diff --git a/packages/block-library/src/cover/edit.native.js b/packages/block-library/src/cover/edit.native.js index 81ff43128b1a3..989c5ec3a0d33 100644 --- a/packages/block-library/src/cover/edit.native.js +++ b/packages/block-library/src/cover/edit.native.js @@ -538,6 +538,7 @@ const Cover = ( { { ( { shouldEnableBottomSheetScroll } ) => ( color ) ), ]; - const mergedColors = [ + const mergedGradients = [ + ...new Set( + ( defaultSettings.gradients ?? [] ).map( + ( { gradient } ) => gradient + ) + ), + ]; + const allAvailableColors = [ ...new Set( ( defaultSettings.allColors ?? [] ).map( ( { color } ) => color ) ), ]; - const defaultGradientColors = [ + const allAvailableGradients = [ ...new Set( - ( defaultSettings.gradients ?? [] ).map( + ( defaultSettings.allGradients ?? [] ).map( ( { gradient } ) => gradient ) ), ]; - const colors = isGradientSegment ? defaultGradientColors : defaultColors; + + const colors = isGradientSegment ? mergedGradients : mergedColors; + const allColors = isGradientSegment + ? allAvailableGradients + : allAvailableColors; const customIndicatorColor = isGradientSegment ? activeColor @@ -110,7 +121,7 @@ function ColorPalette( { function isSelectedCustom() { const isWithinColors = - activeColor && mergedColors && mergedColors.includes( activeColor ); + activeColor && allColors?.includes( activeColor ); if ( enableCustomColor && activeColor ) { if ( isGradientSegment ) { return isGradientColor && ! isWithinColors; diff --git a/packages/components/src/mobile/color-settings/palette.screen.native.js b/packages/components/src/mobile/color-settings/palette.screen.native.js index bc7187fd092b8..fcf03f9ecd448 100644 --- a/packages/components/src/mobile/color-settings/palette.screen.native.js +++ b/packages/components/src/mobile/color-settings/palette.screen.native.js @@ -29,7 +29,6 @@ import { colorsUtils } from './utils'; import styles from './style.scss'; const HIT_SLOP = { top: 8, bottom: 8, left: 8, right: 8 }; -const THEME_PALETTE_NAME = 'Theme'; const PaletteScreen = () => { const route = useRoute(); @@ -48,7 +47,6 @@ const PaletteScreen = () => { const [ currentValue, setCurrentValue ] = useState( colorValue ); const isGradientColor = isGradient( currentValue ); const selectedSegmentIndex = isGradientColor ? 1 : 0; - const allAvailableColors = useMobileGlobalStylesColors(); const [ currentSegment, setCurrentSegment ] = useState( segments[ selectedSegmentIndex ] @@ -57,6 +55,10 @@ const PaletteScreen = () => { const currentSegmentColors = ! isGradientSegment ? defaultSettings.colors : defaultSettings.gradients; + const allAvailableColors = useMobileGlobalStylesColors(); + const allAvailableGradients = currentSegmentColors + .flatMap( ( { gradients } ) => gradients ) + .filter( Boolean ); const horizontalSeparatorStyle = usePreferredColorSchemeStyle( styles.horizontalSeparator, @@ -184,10 +186,10 @@ const PaletteScreen = () => { colors: palette.colors, gradients: palette.gradients, allColors: allAvailableColors, + allGradients: allAvailableGradients, }; - const enableCustomColor = - ! isGradientSegment && - palette.name === THEME_PALETTE_NAME; + // Limit to show the custom indicator to the first available palette + const enableCustomColor = paletteKey === 0; return ( Date: Wed, 10 Jan 2024 09:58:32 -0500 Subject: [PATCH 44/51] Font Library: filter fonts upload directory (#57697) * add global configuration variables for font directory * add multi-site based directory path for fonts * add docblock for get_multi_site_font_sub_dir * Rename function for accuracy. * Use filter instead of constant to determine where fonts are uploaded. * Format php. * simplify the filters code and output the same array as upload_filter does * remove tests no longer used * add a test case for the filter * rename function. Replaces the misleading 'subdir' term by 'dir'. --------- Co-authored-by: madhusudhand Co-authored-by: Matias Benedetto --- .../font-library/class-wp-font-family.php | 4 +- .../font-library/class-wp-font-library.php | 76 +++++++++++++------ .../font-library/wpFontLibrary/fontsDir.php | 70 +++++++++++++++++ .../wpFontLibrary/getFontsDir.php | 18 ----- .../wpFontLibrary/setUploadDir.php | 32 -------- 5 files changed, 126 insertions(+), 74 deletions(-) create mode 100644 phpunit/tests/fonts/font-library/wpFontLibrary/fontsDir.php delete mode 100644 phpunit/tests/fonts/font-library/wpFontLibrary/getFontsDir.php delete mode 100644 phpunit/tests/fonts/font-library/wpFontLibrary/setUploadDir.php diff --git a/lib/experimental/fonts/font-library/class-wp-font-family.php b/lib/experimental/fonts/font-library/class-wp-font-family.php index a4204dfe1fa2c..e47cf0afdac1d 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-family.php +++ b/lib/experimental/fonts/font-library/class-wp-font-family.php @@ -599,9 +599,9 @@ private function create_or_update_font_post() { */ public function install( $files = null ) { add_filter( 'upload_mimes', array( 'WP_Font_Library', 'set_allowed_mime_types' ) ); - add_filter( 'upload_dir', array( 'WP_Font_Library', 'set_upload_dir' ) ); + add_filter( 'upload_dir', array( 'WP_Font_Library', 'fonts_dir' ) ); $were_assets_written = $this->download_or_move_font_faces( $files ); - remove_filter( 'upload_dir', array( 'WP_Font_Library', 'set_upload_dir' ) ); + remove_filter( 'upload_dir', array( 'WP_Font_Library', 'fonts_dir' ) ); remove_filter( 'upload_mimes', array( 'WP_Font_Library', 'set_allowed_mime_types' ) ); if ( ! $were_assets_written ) { diff --git a/lib/experimental/fonts/font-library/class-wp-font-library.php b/lib/experimental/fonts/font-library/class-wp-font-library.php index 59ec5e93fa787..99de81e0bd74a 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-library.php +++ b/lib/experimental/fonts/font-library/class-wp-font-library.php @@ -141,40 +141,72 @@ public static function get_font_collection( $id ) { } /** - * Gets the upload directory for fonts. + * Returns an array containing the current fonts upload directory's path and URL. * * @since 6.5.0 * - * @return string Path of the upload directory for fonts. + * @param array $defaults { + * Array of information about the upload directory. + * + * @type string $path Base directory and subdirectory or full path to the fonts upload directory. + * @type string $url Base URL and subdirectory or absolute URL to the fonts upload directory. + * @type string $subdir Subdirectory + * @type string $basedir Path without subdir. + * @type string $baseurl URL path without subdir. + * @type string|false $error False or error message. + * } + * + * @return array $defaults { + * Array of information about the upload directory. + * + * @type string $path Base directory and subdirectory or full path to the fonts upload directory. + * @type string $url Base URL and subdirectory or absolute URL to the fonts upload directory. + * @type string $subdir Subdirectory + * @type string $basedir Path without subdir. + * @type string $baseurl URL path without subdir. + * @type string|false $error False or error message. + * } */ - public static function get_fonts_dir() { - return path_join( WP_CONTENT_DIR, 'fonts' ); + public static function fonts_dir( $defaults = array() ) { + $site_path = self::get_multi_site_dir(); + + // Sets the defaults. + $defaults['path'] = path_join( WP_CONTENT_DIR, 'fonts' ) . $site_path; + $defaults['url'] = untrailingslashit( content_url( 'fonts' ) ) . $site_path; + $defaults['subdir'] = ''; + $defaults['basedir'] = path_join( WP_CONTENT_DIR, 'fonts' ) . $site_path; + $defaults['baseurl'] = untrailingslashit( content_url( 'fonts' ) ) . $site_path; + $defaults['error'] = false; + + // Filters the fonts directory data. + return apply_filters( 'fonts_dir', $defaults ); } /** - * Sets the upload directory for fonts. + * Gets the Site dir for fonts, using the blog ID if multi-site, empty otherwise. * * @since 6.5.0 * - * @param array $defaults { - * Default upload directory. + * @return string Site dir path. + */ + private static function get_multi_site_dir() { + $font_sub_dir = ''; + if ( is_multisite() && ! ( is_main_network() && is_main_site() ) ) { + $font_sub_dir = '/sites/' . get_current_blog_id(); + } + return $font_sub_dir; + } + + /** + * Gets the upload directory for fonts. * - * @type string $path Path to the directory. - * @type string $url URL for the directory. - * @type string $subdir Sub-directory of the directory. - * @type string $basedir Base directory. - * @type string $baseurl Base URL. - * } - * @return array Modified upload directory. + * @since 6.5.0 + * + * @return string Path of the upload directory for fonts. */ - public static function set_upload_dir( $defaults ) { - $defaults['basedir'] = WP_CONTENT_DIR; - $defaults['baseurl'] = content_url(); - $defaults['subdir'] = '/fonts'; - $defaults['path'] = self::get_fonts_dir(); - $defaults['url'] = $defaults['baseurl'] . '/fonts'; - - return $defaults; + public static function get_fonts_dir() { + $fonts_dir_settings = self::fonts_dir(); + return $fonts_dir_settings['path']; } /** diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/fontsDir.php b/phpunit/tests/fonts/font-library/wpFontLibrary/fontsDir.php new file mode 100644 index 0000000000000..9926bb7409088 --- /dev/null +++ b/phpunit/tests/fonts/font-library/wpFontLibrary/fontsDir.php @@ -0,0 +1,70 @@ +dir_defaults = array( + 'path' => path_join( WP_CONTENT_DIR, 'fonts' ), + 'url' => content_url( 'fonts' ), + 'subdir' => '', + 'basedir' => path_join( WP_CONTENT_DIR, 'fonts' ), + 'baseurl' => content_url( 'fonts' ), + 'error' => false, + ); + } + + public function test_fonts_dir() { + $fonts_dir = WP_Font_Library::fonts_dir(); + $this->assertEquals( $fonts_dir, $this->dir_defaults ); + } + + public function test_fonts_dir_with_filter() { + // Define a callback function to pass to the filter. + function set_new_values( $defaults ) { + $defaults['path'] = '/custom-path/fonts/my-custom-subdir'; + $defaults['url'] = 'http://example.com/custom-path/fonts/my-custom-subdir'; + $defaults['subdir'] = 'my-custom-subdir'; + $defaults['basedir'] = '/custom-path/fonts'; + $defaults['baseurl'] = 'http://example.com/custom-path/fonts'; + $defaults['error'] = false; + return $defaults; + } + + // Add the filter. + add_filter( 'fonts_dir', 'set_new_values' ); + + // Gets the fonts dir. + $fonts_dir = WP_Font_Library::fonts_dir(); + + $expected = array( + 'path' => '/custom-path/fonts/my-custom-subdir', + 'url' => 'http://example.com/custom-path/fonts/my-custom-subdir', + 'subdir' => 'my-custom-subdir', + 'basedir' => '/custom-path/fonts', + 'baseurl' => 'http://example.com/custom-path/fonts', + 'error' => false, + ); + + $this->assertEquals( $fonts_dir, $expected, 'The fonts_dir() method should return the expected values.' ); + + // Remove the filter. + remove_filter( 'fonts_dir', 'set_new_values' ); + + // Gets the fonts dir. + $fonts_dir = WP_Font_Library::fonts_dir(); + + $this->assertEquals( $fonts_dir, $this->dir_defaults, 'The fonts_dir() method should return the default values.' ); + } +} diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontsDir.php b/phpunit/tests/fonts/font-library/wpFontLibrary/getFontsDir.php deleted file mode 100644 index 1200200d7160b..0000000000000 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/getFontsDir.php +++ /dev/null @@ -1,18 +0,0 @@ -assertStringEndsWith( '/wp-content/fonts', WP_Font_Library::get_fonts_dir() ); - } -} diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/setUploadDir.php b/phpunit/tests/fonts/font-library/wpFontLibrary/setUploadDir.php deleted file mode 100644 index 29d481d8afd6b..0000000000000 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/setUploadDir.php +++ /dev/null @@ -1,32 +0,0 @@ - '/abc', - 'basedir' => '/any/path', - 'baseurl' => 'http://example.com/an/arbitrary/url', - 'path' => '/any/path/abc', - 'url' => 'http://example.com/an/arbitrary/url/abc', - ); - $expected = array( - 'subdir' => '/fonts', - 'basedir' => WP_CONTENT_DIR, - 'baseurl' => content_url(), - 'path' => path_join( WP_CONTENT_DIR, 'fonts' ), - 'url' => content_url() . '/fonts', - ); - $this->assertSame( $expected, WP_Font_Library::set_upload_dir( $defaults ) ); - } -} From 714e6b107670e5ee4d92b5126f99c54305fee03b Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 10 Jan 2024 16:08:05 +0100 Subject: [PATCH 45/51] Tooltip: no-op when nested inside another Tooltip component (#57202) * Tooltip: no-op when nested inside another Tooltip component * Fix Storybook control type * Use internal components context system * Add Storybook example * Snapshots * Add nested unit test * CHANGELOG * Avoid ESLint disable * Keep nested tooltip storybook example * isNestedInParentTooltip => isNestedInTooltip * toBeInTheDocument => toBeVisible * "inner" => "nested" * Forward rest props to the tooltip instead of the ancor, update snapshots * Format warning message * Add paragraph in docs * Complete comment --- packages/components/CHANGELOG.md | 1 + packages/components/src/tooltip/README.md | 4 ++ packages/components/src/tooltip/index.tsx | 56 ++++++++++++++++--- .../src/tooltip/stories/index.story.tsx | 19 ++++++- .../components/src/tooltip/test/index.tsx | 46 +++++++++++++++ packages/components/src/tooltip/types.ts | 4 ++ 6 files changed, 120 insertions(+), 10 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index c7c1a515a64ce..a8dd57900cfae 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -32,6 +32,7 @@ - `InputControl`, `NumberControl`, `UnitControl`, `SelectControl`, `TreeSelect`: Add `compact` size variant ([#57398](https://github.com/WordPress/gutenberg/pull/57398)). - `ToggleGroupControl`: Update button size in large variant to be 32px ([#57338](https://github.com/WordPress/gutenberg/pull/57338)). - `Tooltip`: improve unit tests ([#57345](https://github.com/WordPress/gutenberg/pull/57345)). +- `Tooltip`: no-op when nested inside other `Tooltip` components ([#57202](https://github.com/WordPress/gutenberg/pull/57202)). ### Experimental diff --git a/packages/components/src/tooltip/README.md b/packages/components/src/tooltip/README.md index 9b214e8fc6b00..ef2cd35d25543 100644 --- a/packages/components/src/tooltip/README.md +++ b/packages/components/src/tooltip/README.md @@ -16,6 +16,10 @@ const MyTooltip = () => ( ); ``` +### Nested tooltips + +In case one or more `Tooltip` components are rendered inside another `Tooltip` component, only the tooltip associated to the outermost `Tooltip` component will be rendered in the browser and shown to the user appropriately. The rest of the nested `Tooltip` components will simply no-op and pass-through their anchor. + ## Props The component accepts the following props: diff --git a/packages/components/src/tooltip/index.tsx b/packages/components/src/tooltip/index.tsx index 817d6d18812ee..1e652d9a42dbb 100644 --- a/packages/components/src/tooltip/index.tsx +++ b/packages/components/src/tooltip/index.tsx @@ -8,22 +8,37 @@ import * as Ariakit from '@ariakit/react'; * WordPress dependencies */ import { useInstanceId } from '@wordpress/compose'; -import { Children } from '@wordpress/element'; +import { Children, cloneElement } from '@wordpress/element'; import deprecated from '@wordpress/deprecated'; /** * Internal dependencies */ -import type { TooltipProps } from './types'; +import type { TooltipProps, TooltipInternalContext } from './types'; import Shortcut from '../shortcut'; import { positionToPlacement } from '../popover/utils'; +import { + contextConnect, + useContextSystem, + ContextSystemProvider, +} from '../context'; +import type { WordPressComponentProps } from '../context'; /** * Time over anchor to wait before showing tooltip */ export const TOOLTIP_DELAY = 700; -function Tooltip( props: TooltipProps ) { +const CONTEXT_VALUE = { + Tooltip: { + isNestedInTooltip: true, + }, +}; + +function UnconnectedTooltip( + props: WordPressComponentProps< TooltipProps, 'div', false >, + ref: React.ForwardedRef< any > +) { const { children, delay = TOOLTIP_DELAY, @@ -32,7 +47,15 @@ function Tooltip( props: TooltipProps ) { position, shortcut, text, - } = props; + + // From Internal Context system + isNestedInTooltip, + + ...restProps + } = useContextSystem< typeof props & TooltipInternalContext >( + props, + 'Tooltip' + ); const baseId = useInstanceId( Tooltip, 'tooltip' ); const describedById = text || shortcut ? baseId : undefined; @@ -43,7 +66,7 @@ function Tooltip( props: TooltipProps ) { if ( 'development' === process.env.NODE_ENV ) { // eslint-disable-next-line no-console console.error( - 'Tooltip should be called with only a single child element.' + 'wp-components.Tooltip should be called with only a single child element.' ); } } @@ -64,24 +87,37 @@ function Tooltip( props: TooltipProps ) { } computedPlacement = computedPlacement || 'bottom'; - const tooltipStore = Ariakit.useTooltipStore( { + // Removing the `Ariakit` namespace from the hook name allows ESLint to + // properly identify the hook, and apply the correct linting rules. + const useAriakitTooltipStore = Ariakit.useTooltipStore; + const tooltipStore = useAriakitTooltipStore( { placement: computedPlacement, showTimeout: delay, } ); + if ( isNestedInTooltip ) { + return isOnlyChild + ? cloneElement( children, { + ...restProps, + ref, + } ) + : children; + } + return ( - <> + { isOnlyChild ? undefined : children } { isOnlyChild && ( text || shortcut ) && ( ) } - + ); } +export const Tooltip = contextConnect( UnconnectedTooltip, 'Tooltip' ); + export default Tooltip; diff --git a/packages/components/src/tooltip/stories/index.story.tsx b/packages/components/src/tooltip/stories/index.story.tsx index 760f3dcc23e2f..b006bc03aced9 100644 --- a/packages/components/src/tooltip/stories/index.story.tsx +++ b/packages/components/src/tooltip/stories/index.story.tsx @@ -30,7 +30,7 @@ const meta: Meta< typeof Tooltip > = { 'bottom right', ], }, - shortcut: { control: { type: 'text' } }, + shortcut: { control: { type: 'object' } }, }, parameters: { controls: { expanded: true }, @@ -57,3 +57,20 @@ KeyboardShortcut.args = { ariaLabel: shortcutAriaLabel.primaryShift( ',' ), }, }; + +/** + * In case one or more `Tooltip` components are rendered inside another + * `Tooltip` component, only the tooltip associated to the outermost `Tooltip` + * component will be rendered in the browser and shown to the user + * appropriately. The rest of the nested `Tooltip` components will simply no-op + * and pass-through their anchor. + */ +export const Nested: StoryFn< typeof Tooltip > = Template.bind( {} ); +Nested.args = { + children: ( + + + + ), + text: 'Outer tooltip text', +}; diff --git a/packages/components/src/tooltip/test/index.tsx b/packages/components/src/tooltip/test/index.tsx index cbe144cfa53d4..ed6f7b5f7b4a1 100644 --- a/packages/components/src/tooltip/test/index.tsx +++ b/packages/components/src/tooltip/test/index.tsx @@ -436,4 +436,50 @@ describe( 'Tooltip', () => { await waitExpectTooltipToHide(); } ); } ); + + describe( 'nested', () => { + it( 'should render the outer tooltip and ignore nested tooltips', async () => { + render( + + + + + + + + ); + + // Hover the anchor. Only the outer tooltip should show. + await hover( + screen.getByRole( 'button', { + name: 'Tooltip anchor', + } ) + ); + + await waitFor( () => + expect( + screen.getByRole( 'tooltip', { name: 'Outer tooltip' } ) + ).toBeVisible() + ); + expect( + screen.queryByRole( 'tooltip', { name: 'Middle tooltip' } ) + ).not.toBeInTheDocument(); + expect( + screen.queryByRole( 'tooltip', { name: 'Inner tooltip' } ) + ).not.toBeInTheDocument(); + expect( + screen.getByRole( 'button', { + description: 'Outer tooltip', + } ) + ).toBeVisible(); + + // Hover outside of the anchor, tooltip should hide + await hoverOutside(); + await waitFor( () => + expect( + screen.queryByRole( 'tooltip', { name: 'Outer tooltip' } ) + ).not.toBeInTheDocument() + ); + } ); + } ); } ); diff --git a/packages/components/src/tooltip/types.ts b/packages/components/src/tooltip/types.ts index 8708ae7005f5b..3d28a1a0e96c6 100644 --- a/packages/components/src/tooltip/types.ts +++ b/packages/components/src/tooltip/types.ts @@ -59,3 +59,7 @@ export type TooltipProps = { */ text?: string; }; + +export type TooltipInternalContext = { + isNestedInTooltip?: boolean; +}; From c527d4d72949112f02ba3b9c106b538a4c20061e Mon Sep 17 00:00:00 2001 From: Gutenberg Repository Automation Date: Wed, 10 Jan 2024 15:54:18 +0000 Subject: [PATCH 46/51] Update Changelog for 17.5.0-rc.1 --- changelog.txt | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/changelog.txt b/changelog.txt index ba43ddc731d01..9268dc7edd1fb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,261 @@ == Changelog == += 17.5.0-rc.1 = + + + +## Changelog + +### Enhancements + +#### Editor Unification +- Editor: Add the show most used blocks preference to the site editor. ([57637](https://github.com/WordPress/gutenberg/pull/57637)) +- Editor: Migrate and unify the panel preferences. ([57529](https://github.com/WordPress/gutenberg/pull/57529)) +- Editor: Unify context text cursor preference. ([57479](https://github.com/WordPress/gutenberg/pull/57479)) +- Editor: Unify list view open preference. ([57504](https://github.com/WordPress/gutenberg/pull/57504)) +- Editor: Unify right click override preference. ([57468](https://github.com/WordPress/gutenberg/pull/57468)) +- Editor: Unify show icon labels preference. ([57480](https://github.com/WordPress/gutenberg/pull/57480)) +- Editor: Unify spotlight mode preference. ([57533](https://github.com/WordPress/gutenberg/pull/57533)) +- Editor: Unify the distraction free preference. ([57590](https://github.com/WordPress/gutenberg/pull/57590)) +- Editor: Unify the show block breadcrumbs preference. ([57506](https://github.com/WordPress/gutenberg/pull/57506)) +- Editor: Unify the top toolbar preference. ([57531](https://github.com/WordPress/gutenberg/pull/57531)) + +#### Components +- Components: Replace `TabPanel` with `Tabs` in inline color picker. ([57292](https://github.com/WordPress/gutenberg/pull/57292) +- Add `compact` size variant to InputControl-based components. ([57398](https://github.com/WordPress/gutenberg/pull/57398)) +- BaseControl: Connect to context system. ([57408](https://github.com/WordPress/gutenberg/pull/57408)) +- Replace `TabPanel` with `Tabs` in the Style Book. ([57287](https://github.com/WordPress/gutenberg/pull/57287)) +- Tooltip: Improve tests. ([57345](https://github.com/WordPress/gutenberg/pull/57345)) +- Update @ariakit/react to v0.3.12 and @ariakit/test to v0.3.7. ([57547](https://github.com/WordPress/gutenberg/pull/57547)) + +#### Font Library +- Font Library: Remove "has_font_mime_type" function. ([57364](https://github.com/WordPress/gutenberg/pull/57364)) +- Font Library: Update font uninstall modal text. ([57368](https://github.com/WordPress/gutenberg/pull/57368)) +- Font Library: Add progress-bar while uploading font assets. ([57463](https://github.com/WordPress/gutenberg/pull/57463)) +- Font Library: Singularize install font families endpoint. ([57569](https://github.com/WordPress/gutenberg/pull/57569)) +- Font Library: Unregister font collection. ([54701](https://github.com/WordPress/gutenberg/pull/54701)) + +#### Site Editor +- Add Template Modal: Update scroll related layout. ([57617](https://github.com/WordPress/gutenberg/pull/57617)) +- Components: Replace `TabPanel` with `Tabs` in the Font Library `Modal`. ([57181](https://github.com/WordPress/gutenberg/pull/57181)) + +#### Interactivity API +- Implement `wp_initial_state()`. ([57556](https://github.com/WordPress/gutenberg/pull/57556)) +- Server directive processing: Stop processing non-interactive blocks. ([56302](https://github.com/WordPress/gutenberg/pull/56302)) +- Interactive template: Use viewModule. ([57712](https://github.com/WordPress/gutenberg/pull/57712)) +- Navigation Block: Use dom.focus for focus control. ([57362](https://github.com/WordPress/gutenberg/pull/57362)) + + +#### Site Editor +- Site editor: Add padding to entity save panel header. ([57471](https://github.com/WordPress/gutenberg/pull/57471)) +- Site editor: Add margin to entity save panel header via a classname. ([57473](https://github.com/WordPress/gutenberg/pull/57473)) + + +#### Block Library +- Post Featured Image: Add a useFirstImageFromPost attribute. ([56573](https://github.com/WordPress/gutenberg/pull/56573)) +- Gallery Block: Add random order setting. ([57477](https://github.com/WordPress/gutenberg/pull/57477)) +- Image Block: Change upload icon label. ([57704](https://github.com/WordPress/gutenberg/pull/57704)) + + +### Bug Fixes + +- Avoid using a memoized selector without dependencies. ([57257](https://github.com/WordPress/gutenberg/pull/57257)) +- Core Data: Pass the 'options' argument to data action shortcuts. ([57383](https://github.com/WordPress/gutenberg/pull/57383)) +- Preferences: Update accessibility scope to "core". ([57563](https://github.com/WordPress/gutenberg/pull/57563)) + +#### Block Editor +- Fix Link UI displaying out of sync results. ([57522](https://github.com/WordPress/gutenberg/pull/57522)) +- Give iframe fallback background color. ([57330](https://github.com/WordPress/gutenberg/pull/57330)) +- Rich text: Add HTML string methods to RichTextData. ([57322](https://github.com/WordPress/gutenberg/pull/57322)) + +#### Block Library +- Footnotes: Fix wrong link when adding more than 9 footnotes. ([57599](https://github.com/WordPress/gutenberg/pull/57599)) +- Table: Remove unnecessary margin override in editor styles. ([57699](https://github.com/WordPress/gutenberg/pull/57699)) +- Template Part block: Fix template part path arg missing from actions. ([56790](https://github.com/WordPress/gutenberg/pull/56790)) + +#### Components +- DuotonePicker: Fix top margin when no duotone options. ([57489](https://github.com/WordPress/gutenberg/pull/57489)) +- NavigatorProvider: Exclude size value from contain CSS rule. ([57498](https://github.com/WordPress/gutenberg/pull/57498)) +- Snackbar: Fix icon positioning. ([57377](https://github.com/WordPress/gutenberg/pull/57377)) + +#### Patterns +- Pattern Overrides: Add `template-lock: All` to pattern inner blocks to prevent deletion/insertion. ([57661](https://github.com/WordPress/gutenberg/pull/57661)) +- Refactor the findOrCreate term method. ([57655](https://github.com/WordPress/gutenberg/pull/57655)) +- Edit source pattern in focus mode in post and site editors. ([57036](https://github.com/WordPress/gutenberg/pull/57036)) + + +#### Site Editor +- Make sure comamnd palette toggle does not disappear while being clicked. ([57420](https://github.com/WordPress/gutenberg/pull/57420)) +- Reinstate iframe CSS for editor canvas container. ([57503](https://github.com/WordPress/gutenberg/pull/57503)) + +#### Global Styles +- Use `is-layout` pattern on layout generated classname. ([57564](https://github.com/WordPress/gutenberg/pull/57564)) +- Global styles revisions: Add individual headings translations, update tests. ([57472](https://github.com/WordPress/gutenberg/pull/57472)) +- Global style revisions: Move change summary code and tests to block editor package. ([57411](https://github.com/WordPress/gutenberg/pull/57411)) +- Reduce specificity of block style variation selectors. ([57659](https://github.com/WordPress/gutenberg/pull/57659)) +- Background image block support: Add tests for size and repeat output. ([57474](https://github.com/WordPress/gutenberg/pull/57474)) + + +#### Post Editor +- Fix Template preview menu item accessibility. ([57456](https://github.com/WordPress/gutenberg/pull/57456)) +- Fullscreen mode description: Use full text instead of abbreviation. ([57518](https://github.com/WordPress/gutenberg/pull/57518)) +- Improve pre-publish checks naming consistency. ([57019](https://github.com/WordPress/gutenberg/pull/57019)) +- Make the Replace featured image button perceivable by assistive technologies. ([57453](https://github.com/WordPress/gutenberg/pull/57453)) + +#### Components +- Label the HeightControl. ([57683](https://github.com/WordPress/gutenberg/pull/57683)) +- NumberControl: Make increment and decrement buttons keyboard accessible. ([57402](https://github.com/WordPress/gutenberg/pull/57402)) + +#### Block Tools +- Update the position of the patterns tab in the inserter menu. ([55688](https://github.com/WordPress/gutenberg/pull/55688)) +- Use full text instead of abbreviation for min height setting. ([57680](https://github.com/WordPress/gutenberg/pull/57680)) +- ResizableEditor: Fix tab order for resize handles. ([57475](https://github.com/WordPress/gutenberg/pull/57475)) +- Keep Lock button it in the toolbar until unmounted. ([57229](https://github.com/WordPress/gutenberg/pull/57229)) +- Custom field connections: Better description on Experiments page. ([57501](https://github.com/WordPress/gutenberg/pull/57501)) + +### Performance + +#### Block Library +- File: Remove 'block-editor' store subscription. ([57511](https://github.com/WordPress/gutenberg/pull/57511)) +- Remove store subscriptions from Audio and Video blocks. ([57449](https://github.com/WordPress/gutenberg/pull/57449)) +- Site Logo: Remove unnecessary 'block-editor' store subscription. ([57513](https://github.com/WordPress/gutenberg/pull/57513)) +- Send numerical post id when uploading image. ([57388](https://github.com/WordPress/gutenberg/pull/57388)) +- PostFeaturedImage: Remove unnecessary 'block-editor' store subscription. ([57554](https://github.com/WordPress/gutenberg/pull/57554)) + +### Experiments + +#### Data Views +- DataViews: Use DropdownMenuRadioItem component when possible. ([57505](https://github.com/WordPress/gutenberg/pull/57505)) +- Align icon size + placement in Patterns data view. ([57548](https://github.com/WordPress/gutenberg/pull/57548)) +- DataViews: Add `duplicate pattern` action in patterns page. ([57592](https://github.com/WordPress/gutenberg/pull/57592)) +- DataViews: Add duplicate template pattern action. ([57638](https://github.com/WordPress/gutenberg/pull/57638)) +- DataViews: Add footer to Pages sidebar. ([57690](https://github.com/WordPress/gutenberg/pull/57690)) +- DataViews: Add new page button in `Pages`. ([57685](https://github.com/WordPress/gutenberg/pull/57685)) +- DataViews: Add sync filter in patterns page. ([57532](https://github.com/WordPress/gutenberg/pull/57532)) +- DataViews: Consolidate CSS selectors naming schema. ([57651](https://github.com/WordPress/gutenberg/pull/57651)) +- DataViews: Fallback to `(no title)` is there's no rendered title. ([57434](https://github.com/WordPress/gutenberg/pull/57434)) +- DataViews: Hide actions menu upon selecting a layout. ([57418](https://github.com/WordPress/gutenberg/pull/57418)) +- DataViews: Make `fields` dependant on `view.type`. ([57450](https://github.com/WordPress/gutenberg/pull/57450)) +- DataViews: Memoize `onSetSelection`. ([57458](https://github.com/WordPress/gutenberg/pull/57458)) +- DataViews: Prevent unnecessary re-renders of Pagination. ([57454](https://github.com/WordPress/gutenberg/pull/57454)) +- DataViews: Prevent unnecessary re-renders. ([57452](https://github.com/WordPress/gutenberg/pull/57452)) +- DataViews: Update names for `DropdownMenuRadioItemCustom`. ([57416](https://github.com/WordPress/gutenberg/pull/57416)) +- DataViews: Use i18n._x to clarify term "Duplicate". ([57686](https://github.com/WordPress/gutenberg/pull/57686)) +- DataViews: Use in patterns page. ([57333](https://github.com/WordPress/gutenberg/pull/57333)) +- Dataview: Change the stacking order of table header. ([57565](https://github.com/WordPress/gutenberg/pull/57565)) +- Dataviews: Add some client side data handling utils. ([57488](https://github.com/WordPress/gutenberg/pull/57488)) +- Make title display in grid views consistent. ([57553](https://github.com/WordPress/gutenberg/pull/57553)) +- Update Table layout design details. ([57644](https://github.com/WordPress/gutenberg/pull/57644)) +- Update pagination spacing in List layout. ([57670](https://github.com/WordPress/gutenberg/pull/57670)) +- Update table header gap. ([57671](https://github.com/WordPress/gutenberg/pull/57671)) +- [Dataviews] Table layout: Ensure focus is not lost on interaction. ([57340](https://github.com/WordPress/gutenberg/pull/57340)) + +#### Patterns +- [Pattern Overrides] Fix duplication of inner blocks. ([57538](https://github.com/WordPress/gutenberg/pull/57538)) +- [Pattern overrides] Allow multiple attributes overrides. ([57573](https://github.com/WordPress/gutenberg/pull/57573)) + + +### Documentation + +- Add links to additional local dev tools in Block Developement Environment readme. ([57682](https://github.com/WordPress/gutenberg/pull/57682)) +- Add new section to the Quick Start Guide about wp-env. ([57559](https://github.com/WordPress/gutenberg/pull/57559)) +- Block JSON schema: Add renaming key to supports definition. ([57373](https://github.com/WordPress/gutenberg/pull/57373)) +- Break out the Curating the Editor Experience doc into its own How-to Guides section. ([57289](https://github.com/WordPress/gutenberg/pull/57289)) +- Change the slug for the theme.json doc to avoid conflicts. ([57410](https://github.com/WordPress/gutenberg/pull/57410)) +- Docs/tutorial: Fix opposite condition for content generation in render.php. ([57445](https://github.com/WordPress/gutenberg/pull/57445)) +- Docs: Fundamentals of Block Development - Static or Dynamic rendering of a block. ([57250](https://github.com/WordPress/gutenberg/pull/57250)) +- Docs: Update sample code to fix React warning error on Tutorial page. ([57412](https://github.com/WordPress/gutenberg/pull/57412)) +- Fix formatting issue due to incorrect link parsing in the Quick Start Guide. ([57693](https://github.com/WordPress/gutenberg/pull/57693)) +- Fix incorrect heading level in Editor curation documentation. ([57409](https://github.com/WordPress/gutenberg/pull/57409)) +- Fix two typos in tutorial.md. ([57627](https://github.com/WordPress/gutenberg/pull/57627)) +- Fix: Create block getting started links. ([57551](https://github.com/WordPress/gutenberg/pull/57551)) +- Improve the static vs dynamic rendering comment in the block tutorial. ([57284](https://github.com/WordPress/gutenberg/pull/57284)) +- Update copyright year to 2024 in `license.md`. ([57481](https://github.com/WordPress/gutenberg/pull/57481)) +- Update the "Build your first block" tutorial based on user feedback. ([57403](https://github.com/WordPress/gutenberg/pull/57403)) +- Update: Material design icons link. ([57550](https://github.com/WordPress/gutenberg/pull/57550)) + + +### Code Quality +- Editor: Unify the DocumentTools component. ([57214](https://github.com/WordPress/gutenberg/pull/57214)) +- Make getLastFocus and setLastFocus private. ([57612](https://github.com/WordPress/gutenberg/pull/57612)) +- Remove deprecated `behaviors` syntax. ([57165](https://github.com/WordPress/gutenberg/pull/57165)) +- Avoid extra `useMarkPersistent` dispatch calls. ([57435](https://github.com/WordPress/gutenberg/pull/57435)) +- Clean up code editor CSS. ([57519](https://github.com/WordPress/gutenberg/pull/57519)) +- Combine selectors in 'useTransformCommands'. ([57424](https://github.com/WordPress/gutenberg/pull/57424)) + +#### Block Library +- Background image: Add has-background classname when background image is applied. ([57495](https://github.com/WordPress/gutenberg/pull/57495)) +- File: Remove unnecessary synchronization effect. ([57585](https://github.com/WordPress/gutenberg/pull/57585)) +- Navigation: Refactor mobile overlay breakpoints to JS. ([57520](https://github.com/WordPress/gutenberg/pull/57520)) +- Search Block: Remove unused `buttonBehavior` attribute. ([53467](https://github.com/WordPress/gutenberg/pull/53467)) + +#### Patterns +- Improve inserter pattern constants. ([57570](https://github.com/WordPress/gutenberg/pull/57570)) +- Remove duplicate setting for `getPostLinkProps` and prefer stable naming. ([57535](https://github.com/WordPress/gutenberg/pull/57535)) +- Rename `patternBlock` to `patternPost`. ([57568](https://github.com/WordPress/gutenberg/pull/57568)) + +#### Post Editor +- Editor: Use hooks instead of HoCs in 'PostVisibilityCheck'. ([57705](https://github.com/WordPress/gutenberg/pull/57705)) +- Quality: Avoid React warning when changing rendering mode. ([57413](https://github.com/WordPress/gutenberg/pull/57413)) + +#### Block Editor +- Editor: Unify the inserter sidebar. ([57466](https://github.com/WordPress/gutenberg/pull/57466)) +- Remove unused parameters from useOnBlockDrop. ([57527](https://github.com/WordPress/gutenberg/pull/57527)) + +#### List View +- Editor: Unify the list view sidebar between the post and site editors. ([57467](https://github.com/WordPress/gutenberg/pull/57467)) +- Add drag cursor to draggable list items. ([57493](https://github.com/WordPress/gutenberg/pull/57493)) + +### Tools + +- Dependency Extraction Webpack Plugin: Use `import` for module externals. ([57577](https://github.com/WordPress/gutenberg/pull/57577)) +- DependencyExtractionWebpackPlugin: Add true shorthand for requestToExternalModule. ([57593](https://github.com/WordPress/gutenberg/pull/57593)) +- DependencyExtractionWebpackPlugin: Use module for @wordpress/interactivity. ([57602](https://github.com/WordPress/gutenberg/pull/57602)) +- Fix webpack not setting environment.module true. ([57714](https://github.com/WordPress/gutenberg/pull/57714)) +- Modules: Load the import map polyfill when needed. ([57256](https://github.com/WordPress/gutenberg/pull/57256)) +- Blocks: Add handling for block.json viewModule. ([57437](https://github.com/WordPress/gutenberg/pull/57437)) + +#### Testing +- Allowed Patterns end-to-end test - move tests that run with a subset of allowed blocks into a group. ([57496](https://github.com/WordPress/gutenberg/pull/57496)) +- Clean up end-to-end tests package. ([57575](https://github.com/WordPress/gutenberg/pull/57575)) +- Fix flaky 'Post publish button' end-to-end test. ([57407](https://github.com/WordPress/gutenberg/pull/57407)) +- Migrate 'allowed patterns' end-to-end tests to Playwright. ([57399](https://github.com/WordPress/gutenberg/pull/57399)) +- Migrate 'block editor keyboard shortcuts' end-to-end tests to Playwright. ([57422](https://github.com/WordPress/gutenberg/pull/57422)) +- Migrate 'core settings' end-to-end tests to Playwright. ([57581](https://github.com/WordPress/gutenberg/pull/57581)) +- Migrate 'datepicker' end-to-end tests to Playwright. ([57545](https://github.com/WordPress/gutenberg/pull/57545)) +- Migrate 'dropdown menu' end-to-end tests to Playwright. ([57663](https://github.com/WordPress/gutenberg/pull/57663)) +- Migrate 'editor modes' end-to-end tests to Playwright. ([57574](https://github.com/WordPress/gutenberg/pull/57574)) +- Migrate 'invalid blocks' end-to-end tests to Playwright. ([57508](https://github.com/WordPress/gutenberg/pull/57508)) +- Migrate 'nux' end-to-end tests to Playwright. ([57542](https://github.com/WordPress/gutenberg/pull/57542)) +- Migrate 'preferences' end-to-end tests to Playwright. ([57446](https://github.com/WordPress/gutenberg/pull/57446)) +- Migrate 'publishing' end-to-end tests to Playwright. ([57521](https://github.com/WordPress/gutenberg/pull/57521)) +- Migrate 'scheduling' end-to-end tests to Playwright. ([57539](https://github.com/WordPress/gutenberg/pull/57539)) +- Migrate 'sidebar' end-to-end tests to Playwright. ([57448](https://github.com/WordPress/gutenberg/pull/57448)) +- Migrate 'taxonomies' end-to-end tests to Playwright. ([57662](https://github.com/WordPress/gutenberg/pull/57662)) +- Migrate `editing-widgets` to Playwright. ([57483](https://github.com/WordPress/gutenberg/pull/57483)) +- Migrate remaining 'publish panel' end-to-end tests to Playwright. ([57432](https://github.com/WordPress/gutenberg/pull/57432)) +- Update 'missing block' end-to-end tests to use the 'setContent' helper. ([57509](https://github.com/WordPress/gutenberg/pull/57509)) + +#### Build Tooling +- Group GitHub Action Dependabot updates. ([57591](https://github.com/WordPress/gutenberg/pull/57591)) +- WP Scripts: Build block.json viewModule. ([57461](https://github.com/WordPress/gutenberg/pull/57461)) +- Dependency Extraction Webpack Plugin: Add Module support. ([57199](https://github.com/WordPress/gutenberg/pull/57199)) + +## First time contributors + +The following PRs were merged by first time contributors: + +- @HrithikDalal: Font Library: Update font uninstall modal text. ([57368](https://github.com/WordPress/gutenberg/pull/57368)) +- @muhme: Fix two typos in tutorial.md. ([57627](https://github.com/WordPress/gutenberg/pull/57627)) + + +## Contributors + +The following contributors merged PRs in this release: + +@afercia @andrewhayward @andrewserong @atachibana @c4rl0sbr4v0 @carolinan @chad1008 @ciampo @DAreRodz @dcalhoun @derekblank @desrosj @ellatrix @fai-sal @fluiddot @geriux @getdave @glendaviesnz @gziolo @hbhalodia @HrithikDalal @jameskoster @jeryj @jorgefilipecosta @jsnajdr @juanmaguitar @kevin940726 @Mamaduka @matiasbenedetto @mcsf @michalczaplinski @mirka @muhme @ndiego @ntsekouras @oandregal @ockham @ramonjd @scruffian @sirreal @Soean @t-hamano @talldan @tellthemachines @youknowriad + + = 17.3.2 = ## Changelog From d8ef7d615049afa5c6903530c8099ca7b69073c7 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Wed, 10 Jan 2024 16:05:09 -0300 Subject: [PATCH 47/51] Font Library: add wp_get_font_dir() function. (#57730) * adding wp_font_dir() function * use wp_font_dir() instead of Font Library method * update php unit teests * rename function --- .../font-library/class-wp-font-family.php | 9 ++- .../font-library/class-wp-font-library.php | 67 ------------------- ...class-wp-rest-font-families-controller.php | 6 +- .../fonts/font-library/font-library.php | 49 ++++++++++++++ .../{wpFontLibrary => }/fontsDir.php | 22 +++--- .../fonts/font-library/wpFontFamily/base.php | 2 +- .../wpRestFontFamiliesController/base.php | 2 +- 7 files changed, 69 insertions(+), 88 deletions(-) rename phpunit/tests/fonts/font-library/{wpFontLibrary => }/fontsDir.php (67%) diff --git a/lib/experimental/fonts/font-library/class-wp-font-family.php b/lib/experimental/fonts/font-library/class-wp-font-family.php index e47cf0afdac1d..f64aebc0c8efa 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-family.php +++ b/lib/experimental/fonts/font-library/class-wp-font-family.php @@ -135,7 +135,7 @@ public function uninstall() { */ private static function delete_asset( $src ) { $filename = basename( $src ); - $file_path = path_join( WP_Font_Library::get_fonts_dir(), $filename ); + $file_path = path_join( wp_get_font_dir()['path'], $filename ); wp_delete_file( $file_path ); @@ -163,7 +163,6 @@ private static function delete_font_face_assets( $font_face ) { return true; } - /** * Gets the overrides for the 'wp_handle_upload' function. * @@ -394,7 +393,7 @@ private function download_or_move_font_faces( $files ) { // If the font face requires the use of the filesystem, create the fonts dir if it doesn't exist. if ( ! empty( $font_face['downloadFromUrl'] ) && ! empty( $font_face['uploadedFile'] ) ) { - wp_mkdir_p( WP_Font_Library::get_fonts_dir() ); + wp_mkdir_p( wp_get_font_dir()['path'] ); } // If installing google fonts, download the font face assets. @@ -599,9 +598,9 @@ private function create_or_update_font_post() { */ public function install( $files = null ) { add_filter( 'upload_mimes', array( 'WP_Font_Library', 'set_allowed_mime_types' ) ); - add_filter( 'upload_dir', array( 'WP_Font_Library', 'fonts_dir' ) ); + add_filter( 'upload_dir', 'wp_get_font_dir' ); $were_assets_written = $this->download_or_move_font_faces( $files ); - remove_filter( 'upload_dir', array( 'WP_Font_Library', 'fonts_dir' ) ); + remove_filter( 'upload_dir', 'wp_get_font_dir' ); remove_filter( 'upload_mimes', array( 'WP_Font_Library', 'set_allowed_mime_types' ) ); if ( ! $were_assets_written ) { diff --git a/lib/experimental/fonts/font-library/class-wp-font-library.php b/lib/experimental/fonts/font-library/class-wp-font-library.php index 99de81e0bd74a..01ee702673e57 100644 --- a/lib/experimental/fonts/font-library/class-wp-font-library.php +++ b/lib/experimental/fonts/font-library/class-wp-font-library.php @@ -140,74 +140,7 @@ public static function get_font_collection( $id ) { return new WP_Error( 'font_collection_not_found', 'Font collection not found.' ); } - /** - * Returns an array containing the current fonts upload directory's path and URL. - * - * @since 6.5.0 - * - * @param array $defaults { - * Array of information about the upload directory. - * - * @type string $path Base directory and subdirectory or full path to the fonts upload directory. - * @type string $url Base URL and subdirectory or absolute URL to the fonts upload directory. - * @type string $subdir Subdirectory - * @type string $basedir Path without subdir. - * @type string $baseurl URL path without subdir. - * @type string|false $error False or error message. - * } - * - * @return array $defaults { - * Array of information about the upload directory. - * - * @type string $path Base directory and subdirectory or full path to the fonts upload directory. - * @type string $url Base URL and subdirectory or absolute URL to the fonts upload directory. - * @type string $subdir Subdirectory - * @type string $basedir Path without subdir. - * @type string $baseurl URL path without subdir. - * @type string|false $error False or error message. - * } - */ - public static function fonts_dir( $defaults = array() ) { - $site_path = self::get_multi_site_dir(); - - // Sets the defaults. - $defaults['path'] = path_join( WP_CONTENT_DIR, 'fonts' ) . $site_path; - $defaults['url'] = untrailingslashit( content_url( 'fonts' ) ) . $site_path; - $defaults['subdir'] = ''; - $defaults['basedir'] = path_join( WP_CONTENT_DIR, 'fonts' ) . $site_path; - $defaults['baseurl'] = untrailingslashit( content_url( 'fonts' ) ) . $site_path; - $defaults['error'] = false; - - // Filters the fonts directory data. - return apply_filters( 'fonts_dir', $defaults ); - } - - /** - * Gets the Site dir for fonts, using the blog ID if multi-site, empty otherwise. - * - * @since 6.5.0 - * - * @return string Site dir path. - */ - private static function get_multi_site_dir() { - $font_sub_dir = ''; - if ( is_multisite() && ! ( is_main_network() && is_main_site() ) ) { - $font_sub_dir = '/sites/' . get_current_blog_id(); - } - return $font_sub_dir; - } - /** - * Gets the upload directory for fonts. - * - * @since 6.5.0 - * - * @return string Path of the upload directory for fonts. - */ - public static function get_fonts_dir() { - $fonts_dir_settings = self::fonts_dir(); - return $fonts_dir_settings['path']; - } /** * Sets the allowed mime types for fonts. diff --git a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php b/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php index 0147d80b7bde9..ede8762c88c6d 100644 --- a/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php +++ b/lib/experimental/fonts/font-library/class-wp-rest-font-families-controller.php @@ -276,7 +276,7 @@ public function update_font_library_permissions_check() { * @return bool Whether the font directory exists. */ private function has_upload_directory() { - $upload_dir = WP_Font_Library::get_fonts_dir(); + $upload_dir = wp_get_font_dir()['path']; return is_dir( $upload_dir ); } @@ -290,7 +290,7 @@ private function has_upload_directory() { private function has_write_permission() { // The update endpoints requires write access to the temp and the fonts directories. $temp_dir = get_temp_dir(); - $upload_dir = WP_Font_Library::get_fonts_dir(); + $upload_dir = wp_get_font_dir()['path']; if ( ! is_writable( $temp_dir ) || ! wp_is_writable( $upload_dir ) ) { return false; } @@ -353,7 +353,7 @@ public function install_fonts( $request ) { } if ( $this->needs_write_permission( $font_family_settings ) ) { - $upload_dir = WP_Font_Library::get_fonts_dir(); + $upload_dir = wp_get_font_dir()['path']; if ( ! $this->has_upload_directory() ) { if ( ! wp_mkdir_p( $upload_dir ) ) { $errors[] = new WP_Error( diff --git a/lib/experimental/fonts/font-library/font-library.php b/lib/experimental/fonts/font-library/font-library.php index 711a6bb40c282..4a2f1be69ef71 100644 --- a/lib/experimental/fonts/font-library/font-library.php +++ b/lib/experimental/fonts/font-library/font-library.php @@ -82,3 +82,52 @@ function wp_unregister_font_collection( $collection_id ) { ); wp_register_font_collection( $default_font_collection ); + +// @core-merge: This code should probably go into Core's src/wp-includes/functions.php. +if ( ! function_exists( 'wp_get_font_dir' ) ) { + /** + * Returns an array containing the current fonts upload directory's path and URL. + * + * @since 6.5.0 + * + * @param array $defaults { + * Array of information about the upload directory. + * + * @type string $path Base directory and subdirectory or full path to the fonts upload directory. + * @type string $url Base URL and subdirectory or absolute URL to the fonts upload directory. + * @type string $subdir Subdirectory + * @type string $basedir Path without subdir. + * @type string $baseurl URL path without subdir. + * @type string|false $error False or error message. + * } + * + * @return array $defaults { + * Array of information about the upload directory. + * + * @type string $path Base directory and subdirectory or full path to the fonts upload directory. + * @type string $url Base URL and subdirectory or absolute URL to the fonts upload directory. + * @type string $subdir Subdirectory + * @type string $basedir Path without subdir. + * @type string $baseurl URL path without subdir. + * @type string|false $error False or error message. + * } + */ + function wp_get_font_dir( $defaults = array() ) { + // Multi site path + $site_path = ''; + if ( is_multisite() && ! ( is_main_network() && is_main_site() ) ) { + $site_path = '/sites/' . get_current_blog_id(); + } + + // Sets the defaults. + $defaults['path'] = path_join( WP_CONTENT_DIR, 'fonts' ) . $site_path; + $defaults['url'] = untrailingslashit( content_url( 'fonts' ) ) . $site_path; + $defaults['subdir'] = ''; + $defaults['basedir'] = path_join( WP_CONTENT_DIR, 'fonts' ) . $site_path; + $defaults['baseurl'] = untrailingslashit( content_url( 'fonts' ) ) . $site_path; + $defaults['error'] = false; + + // Filters the fonts directory data. + return apply_filters( 'font_dir', $defaults ); + } +} diff --git a/phpunit/tests/fonts/font-library/wpFontLibrary/fontsDir.php b/phpunit/tests/fonts/font-library/fontsDir.php similarity index 67% rename from phpunit/tests/fonts/font-library/wpFontLibrary/fontsDir.php rename to phpunit/tests/fonts/font-library/fontsDir.php index 9926bb7409088..5c13f1d120f9a 100644 --- a/phpunit/tests/fonts/font-library/wpFontLibrary/fontsDir.php +++ b/phpunit/tests/fonts/font-library/fontsDir.php @@ -1,6 +1,6 @@ assertEquals( $fonts_dir, $this->dir_defaults ); + $font_dir = wp_get_font_dir(); + $this->assertEquals( $font_dir, $this->dir_defaults ); } public function test_fonts_dir_with_filter() { @@ -43,10 +43,10 @@ function set_new_values( $defaults ) { } // Add the filter. - add_filter( 'fonts_dir', 'set_new_values' ); + add_filter( 'font_dir', 'set_new_values' ); // Gets the fonts dir. - $fonts_dir = WP_Font_Library::fonts_dir(); + $font_dir = wp_get_font_dir(); $expected = array( 'path' => '/custom-path/fonts/my-custom-subdir', @@ -57,14 +57,14 @@ function set_new_values( $defaults ) { 'error' => false, ); - $this->assertEquals( $fonts_dir, $expected, 'The fonts_dir() method should return the expected values.' ); + $this->assertEquals( $font_dir, $expected, 'The wp_get_font_dir() method should return the expected values.' ); // Remove the filter. - remove_filter( 'fonts_dir', 'set_new_values' ); + remove_filter( 'font_dir', 'set_new_values' ); // Gets the fonts dir. - $fonts_dir = WP_Font_Library::fonts_dir(); + $font_dir = wp_get_font_dir(); - $this->assertEquals( $fonts_dir, $this->dir_defaults, 'The fonts_dir() method should return the default values.' ); + $this->assertEquals( $font_dir, $this->dir_defaults, 'The wp_get_font_dir() method should return the default values.' ); } } diff --git a/phpunit/tests/fonts/font-library/wpFontFamily/base.php b/phpunit/tests/fonts/font-library/wpFontFamily/base.php index 3650ac7dab997..3f6ff153fa12f 100644 --- a/phpunit/tests/fonts/font-library/wpFontFamily/base.php +++ b/phpunit/tests/fonts/font-library/wpFontFamily/base.php @@ -28,7 +28,7 @@ abstract class WP_Font_Family_UnitTestCase extends WP_UnitTestCase { public static function set_up_before_class() { parent::set_up_before_class(); - static::$fonts_dir = WP_Font_Library::get_fonts_dir(); + static::$fonts_dir = wp_get_font_dir()['path']; wp_mkdir_p( static::$fonts_dir ); } diff --git a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/base.php b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/base.php index 5ab71a4379851..e2d190cd76af1 100644 --- a/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/base.php +++ b/phpunit/tests/fonts/font-library/wpRestFontFamiliesController/base.php @@ -18,7 +18,7 @@ abstract class WP_REST_Font_Families_Controller_UnitTestCase extends WP_UnitTest public function set_up() { parent::set_up(); - static::$fonts_dir = WP_Font_Library::get_fonts_dir(); + static::$fonts_dir = wp_get_font_dir()['path']; // Create a user with administrator role. $admin_id = $this->factory->user->create( From b892bd8be90d2ab26e4a3de9ae1fdbf7a3ec8542 Mon Sep 17 00:00:00 2001 From: Jerry Jones Date: Wed, 10 Jan 2024 13:50:09 -0600 Subject: [PATCH 48/51] Display a disabled lock button if user cannot control lock state (#57274) --- .../src/components/block-lock/toolbar.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/block-lock/toolbar.js b/packages/block-editor/src/components/block-lock/toolbar.js index ccf04c5e5262d..69f0825ed4be3 100644 --- a/packages/block-editor/src/components/block-lock/toolbar.js +++ b/packages/block-editor/src/components/block-lock/toolbar.js @@ -33,16 +33,24 @@ export default function BlockLockToolbar( { clientId } ) { } }, [ isLocked ] ); - if ( ! canLock || ( ! isLocked && ! hasLockButtonShown.current ) ) { + if ( ! isLocked && ! hasLockButtonShown.current ) { return null; } + let label = isLocked ? __( 'Unlock' ) : __( 'Lock' ); + + if ( ! canLock && isLocked ) { + label = __( 'Locked' ); + } + return ( <> Date: Wed, 10 Jan 2024 20:28:26 -0500 Subject: [PATCH 49/51] docs: Note the mobile E2E tests `jq` dependency (#57741) --- packages/react-native-editor/__device-tests__/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native-editor/__device-tests__/README.md b/packages/react-native-editor/__device-tests__/README.md index e917a297a491c..719adbbcf9426 100644 --- a/packages/react-native-editor/__device-tests__/README.md +++ b/packages/react-native-editor/__device-tests__/README.md @@ -6,6 +6,7 @@ The Mobile Gutenberg (MG) project maintains a suite of automated end-to-end (E2E 1. Complete the [React Native Getting Started](https://reactnative.dev/docs/environment-setup) guide for both iOS and Android, which covers setting up Xcode, Android Studio, the Android SDK. 1. Open [Xcode settings](https://developer.apple.com/documentation/xcode/installing-additional-simulator-runtimes#Install-and-manage-Simulator-runtimes-in-settings) to install the iOS 16.2 simulator runtime. +1. Install [`jq`](https://jqlang.github.io/jq/download/) via [Homebrew](https://brew.sh/) or your preferred package manager. 1. `npm run native test:e2e:setup` ## Running Tests From d03285c8f6c75c0d6801186c992bc243ddf874ae Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 10 Jan 2024 21:01:07 -0500 Subject: [PATCH 50/51] chore: Enable offline media upload features (#57731) * chore: Enable offline status indicator * docs: Add change log entry --- packages/edit-post/src/components/layout/index.native.js | 5 +---- packages/react-native-editor/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/edit-post/src/components/layout/index.native.js b/packages/edit-post/src/components/layout/index.native.js index 50472f7924930..6821aa754eea7 100644 --- a/packages/edit-post/src/components/layout/index.native.js +++ b/packages/edit-post/src/components/layout/index.native.js @@ -152,10 +152,7 @@ class Layout extends Component { onLayout={ this.onRootViewLayout } > - { - // eslint-disable-next-line no-undef - __DEV__ && - } + { isHtmlView ? this.renderHTML() : this.renderVisual() } { ! isHtmlView && Platform.OS === 'android' && ( diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index a62838b066bee..7a1f6997d2a13 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -12,6 +12,7 @@ For each user feature we should also add a importance categorization label to i ## Unreleased - [**] Image block media uploads display a custom error message when there is no internet connection [#56937] - [*] Fix missing custom color indicator for custom gradients [#57605] +- [**] Display a notice when a network connection unavailable [#56934] ## 1.110.0 - [*] [internal] Move InserterButton from components package to block-editor package [#56494] From 19ad158863c86152b7fb26ae575696b031068078 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:47:00 +0900 Subject: [PATCH 51/51] Add description to the save panel header when nothing is checked (#57716) * Add description to the save panel header when nothing is checked * Shortening the description --- .../src/components/entities-saved-states/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/editor/src/components/entities-saved-states/index.js b/packages/editor/src/components/entities-saved-states/index.js index b5f3ea5b433e1..8e531ce580101 100644 --- a/packages/editor/src/components/entities-saved-states/index.js +++ b/packages/editor/src/components/entities-saved-states/index.js @@ -213,13 +213,13 @@ export function EntitiesSavedStatesExtensible( { { __( 'Are you ready to save?' ) } { additionalPrompt } - { isDirty && ( -

- { __( - 'The following changes have been made to your site, templates, and content.' - ) } -

- ) } +

+ { isDirty + ? __( + 'The following changes have been made to your site, templates, and content.' + ) + : __( 'Select the items you want to save.' ) } +

{ sortedPartitionedSavables.map( ( list ) => {