diff --git a/changelog.txt b/changelog.txt index 4a23e8b0345a21..1d40cafd8a0abd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,20 @@ == Changelog == += 15.2.3 = + +## Changelog + +### Bug Fixes + +- useAsyncList: flush state updates when processing queue (https://github.com/WordPress/gutenberg/pull/48238). + +## Contributors + +The following contributors merged PRs in this release: + +@jsnajdr + + = 15.2.2 = diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index cfb95389ba0391..c76c4e7fbccf30 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -1496,11 +1496,13 @@ _Parameters_ ### selectPreviousBlock Yields action objects used in signalling that the block preceding the given -clientId should be selected. +clientId (or optionally, its first parent from bottom to top) +should be selected. _Parameters_ - _clientId_ `string`: Block client ID. +- _orFirstParent_ `boolean`: If true, select the first parent if there is no previous block. ### setBlockMovingClientId diff --git a/lib/block-supports/duotone.php b/lib/block-supports/duotone.php index e0dea9e97cc2bb..d973deb8416848 100644 --- a/lib/block-supports/duotone.php +++ b/lib/block-supports/duotone.php @@ -311,6 +311,9 @@ function gutenberg_get_duotone_filter_id( $preset ) { * @return string Duotone CSS filter property url value. */ function gutenberg_get_duotone_filter_property( $preset ) { + if ( isset( $preset['colors'] ) && 'unset' === $preset['colors'] ) { + return 'none'; + } $filter_id = gutenberg_get_duotone_filter_id( $preset ); return "url('#" . $filter_id . "')"; } @@ -449,16 +452,16 @@ function gutenberg_render_duotone_support( $block_content, $block ) { $duotone_attr = $block['attrs']['style']['color']['duotone']; $is_duotone_colors_array = is_array( $duotone_attr ); - $is_duotone_unset = 'unset' === $duotone_attr; - $is_duotone_preset = ! $is_duotone_colors_array && ! $is_duotone_unset; + $is_duotone_preset = ! $is_duotone_colors_array && strpos( $duotone_attr, 'var:preset|duotone|' ) === 0; if ( $is_duotone_preset ) { + $slug = str_replace( 'var:preset|duotone|', '', $duotone_attr ); $filter_preset = array( - 'slug' => $duotone_attr, + 'slug' => $slug, ); // Utilise existing CSS custom property. - $filter_property = "var(--wp--preset--duotone--$duotone_attr)"; + $filter_property = "var(--wp--preset--duotone--$slug)"; } else { // Handle when Duotone is either: // - "unset" @@ -472,7 +475,7 @@ function gutenberg_render_duotone_support( $block_content, $block ) { ); // Build a customised CSS filter property for unique slug. - $filter_property = $is_duotone_unset ? 'none' : gutenberg_get_duotone_filter_property( $filter_preset ); + $filter_property = gutenberg_get_duotone_filter_property( $filter_preset ); } // - Applied as a class attribute to the block wrapper. diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 1c8de2e2c34121..cb9f1573e4e056 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -301,23 +301,6 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support return ''; } -/** - * Gets classname from last tag in a string of HTML. - * - * @param string $html markup to be processed. - * @return string String of inner wrapper classnames. - */ -function gutenberg_get_classnames_from_last_tag( $html ) { - $tags = new WP_HTML_Tag_Processor( $html ); - $last_classnames = ''; - - while ( $tags->next_tag() ) { - $last_classnames = $tags->get_attribute( 'class' ); - } - - return (string) $last_classnames; -} - /** * Renders the layout config to the block wrapper. * @@ -502,7 +485,17 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { * The first chunk of innerContent contains the block markup up until the inner blocks start. * We want to target the opening tag of the inner blocks wrapper, which is the last tag in that chunk. */ - $inner_content_classnames = isset( $block['innerContent'][0] ) && 'string' === gettype( $block['innerContent'][0] ) ? gutenberg_get_classnames_from_last_tag( $block['innerContent'][0] ) : ''; + $inner_content_classnames = ''; + + if ( isset( $block['innerContent'][0] ) && 'string' === gettype( $block['innerContent'][0] ) && count( $block['innerContent'] ) > 1 ) { + $tags = new WP_HTML_Tag_Processor( $block['innerContent'][0] ); + $last_classnames = ''; + while ( $tags->next_tag() ) { + $last_classnames = $tags->get_attribute( 'class' ); + } + + $inner_content_classnames = (string) $last_classnames; + } $content = $content_with_outer_classnames ? new WP_HTML_Tag_Processor( $content_with_outer_classnames ) : new WP_HTML_Tag_Processor( $block_content ); diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 7334cb6e97cde5..4d688bb5b8b83c 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -114,18 +114,10 @@ class WP_Theme_JSON_Gutenberg { * `use_default_names` preset key, and simplified the metadata structure. * @since 6.0.0 Replaced `override` with `prevent_override` and updated the * `prevent_override` value for `color.duotone` to use `color.defaultDuotone`. + * @since 6.2.0 Added 'shadow' presets. * @var array */ const PRESETS_METADATA = array( - array( - 'path' => array( 'shadow', 'presets' ), - 'prevent_override' => array( 'shadow', 'defaultPresets' ), - 'use_default_names' => false, - 'value_key' => 'shadow', - 'css_vars' => '--wp--preset--shadow--$slug', - 'classes' => array(), - 'properties' => array( 'box-shadow' ), - ), array( 'path' => array( 'color', 'palette' ), 'prevent_override' => array( 'color', 'defaultPalette' ), @@ -184,6 +176,15 @@ class WP_Theme_JSON_Gutenberg { 'classes' => array(), 'properties' => array( 'padding', 'margin' ), ), + array( + 'path' => array( 'shadow', 'presets' ), + 'prevent_override' => array( 'shadow', 'defaultPresets' ), + 'use_default_names' => false, + 'value_key' => 'shadow', + 'css_vars' => '--wp--preset--shadow--$slug', + 'classes' => array(), + 'properties' => array( 'box-shadow' ), + ), ); /** @@ -200,7 +201,8 @@ class WP_Theme_JSON_Gutenberg { * @since 6.1.0 Added the `border-*-color`, `border-*-width`, `border-*-style`, * `--wp--style--root--padding-*`, and `box-shadow` properties, * removed the `--wp--style--block-gap` property. - * @since 6.2.0 Added `min-height`. + * @since 6.2.0 Added `outline-*`, and `min-height` properties. + * * @var array */ const PROPERTIES_METADATA = array( @@ -259,32 +261,18 @@ class WP_Theme_JSON_Gutenberg { 'box-shadow' => array( 'shadow' ), ); - /** - * Protected style properties. - * - * These style properties are only rendered if a setting enables it - * via a value other than `null`. - * - * Each element maps the style property to the corresponding theme.json - * setting key. - * - * @since 5.9.0 - */ - const PROTECTED_PROPERTIES = array( - 'spacing.blockGap' => array( 'spacing', 'blockGap' ), - ); - /** * Indirect metadata for style properties that are not directly output. * - * Each element is a direct mapping from a CSS property name to the - * path to the value in theme.json & block attributes. + * Each element maps from a CSS property name to an array of + * paths to the value in theme.json & block attributes. * * Indirect properties are not output directly by `compute_style_properties`, * but are used elsewhere in the processing of global styles. The indirect * property is used to validate whether or not a style value is allowed. * * @since 6.2.0 + * * @var array */ const INDIRECT_PROPERTIES_METADATA = array( @@ -303,6 +291,21 @@ class WP_Theme_JSON_Gutenberg { ), ); + /** + * Protected style properties. + * + * These style properties are only rendered if a setting enables it + * via a value other than `null`. + * + * Each element maps the style property to the corresponding theme.json + * setting key. + * + * @since 5.9.0 + */ + const PROTECTED_PROPERTIES = array( + 'spacing.blockGap' => array( 'spacing', 'blockGap' ), + ); + /** * The top-level keys a theme.json can have. * @@ -317,8 +320,8 @@ class WP_Theme_JSON_Gutenberg { 'settings', 'styles', 'templateParts', - 'version', 'title', + 'version', ); /** @@ -330,7 +333,8 @@ class WP_Theme_JSON_Gutenberg { * and `typography`, and renamed others according to the new schema. * @since 6.0.0 Added `color.defaultDuotone`. * @since 6.1.0 Added `layout.definitions` and `useRootPaddingAwareAlignments`. - * @since 6.2.0 Added `dimensions.minHeight`, `position.fixed` and `position.sticky`. + * @since 6.2.0 Added `dimensions.minHeight`, 'shadow.presets', 'shadow.defaultPresets', + * `position.fixed` and `position.sticky`. * @var array */ const VALID_SETTINGS = array( @@ -342,10 +346,6 @@ class WP_Theme_JSON_Gutenberg { 'style' => null, 'width' => null, ), - 'shadow' => array( - 'presets' => null, - 'defaultPresets' => null, - ), 'color' => array( 'background' => null, 'custom' => null, @@ -382,6 +382,10 @@ class WP_Theme_JSON_Gutenberg { 'padding' => null, 'units' => null, ), + 'shadow' => array( + 'presets' => null, + 'defaultPresets' => null, + ), 'typography' => array( 'fluid' => null, 'customFontSize' => null, @@ -407,7 +411,8 @@ class WP_Theme_JSON_Gutenberg { * @since 6.1.0 Added new side properties for `border`, * added new property `shadow`, * updated `blockGap` to be allowed at any level. - * @since 6.2.0 Added `dimensions.minHeight`. + * @since 6.2.0 Added `outline`, and `minHeight` properties. + * * @var array */ const VALID_STYLES = array( @@ -426,7 +431,6 @@ class WP_Theme_JSON_Gutenberg { 'gradient' => null, 'text' => null, ), - 'css' => null, 'dimensions' => array( 'minHeight' => null, ), @@ -455,6 +459,7 @@ class WP_Theme_JSON_Gutenberg { 'textDecoration' => null, 'textTransform' => null, ), + 'css' => null, ); /** @@ -888,7 +893,6 @@ protected static function get_blocks_metadata() { } static::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = implode( ',', $element_selector ); } - // If the block has style variations, append their selectors to the block metadata. if ( ! empty( $block_type->styles ) ) { $style_selectors = array(); @@ -966,27 +970,6 @@ public function get_settings() { } } - /** - * Processes the CSS, to apply nesting. - * - * @param string $css The CSS to process. - * @param string $selector The selector to nest. - * - * @return string The processed CSS. - */ - protected function process_blocks_custom_css( $css, $selector ) { - $processed_css = ''; - - // Split CSS nested rules. - $parts = explode( '&', $css ); - foreach ( $parts as $part ) { - $processed_css .= ( ! str_contains( $part, '{' ) ) - ? trim( $selector ) . '{' . trim( $part ) . '}' // If the part doesn't contain braces, it applies to the root level. - : trim( $selector . $part ); // Prepend the selector, which effectively replaces the "&" character. - } - return $processed_css; - } - /** * Returns the stylesheet that results of processing * the theme.json structure this object represents. @@ -1093,12 +1076,34 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' return $stylesheet; } + /** + * Processes the CSS, to apply nesting. + * + * @since 6.2.0 + * + * @param string $css The CSS to process. + * @param string $selector The selector to nest. + * @return string The processed CSS. + */ + protected function process_blocks_custom_css( $css, $selector ) { + $processed_css = ''; + + // Split CSS nested rules. + $parts = explode( '&', $css ); + foreach ( $parts as $part ) { + $processed_css .= ( ! str_contains( $part, '{' ) ) + ? trim( $selector ) . '{' . trim( $part ) . '}' // If the part doesn't contain braces, it applies to the root level. + : trim( $selector . $part ); // Prepend the selector, which effectively replaces the "&" character. + } + return $processed_css; + } + /** * Returns the global styles custom css. * * @since 6.2.0 * - * @return string + * @return string The global styles custom CSS. */ public function get_custom_css() { // Add the global styles root CSS. @@ -1114,6 +1119,7 @@ public function get_custom_css() { } } } + return $stylesheet; } @@ -2344,10 +2350,10 @@ static function( $split_feature_selector ) use ( $clean_style_variation_selector $new_feature_declarations = static::compute_style_properties( array( $feature_name => $style_variation_node[ $feature_name ] ), $settings, null, $this->theme_json ); /* - * Merge new declarations with any that already exist for - * the feature selector. This may occur when multiple block - * support features use the same custom selector. - */ + * Merge new declarations with any that already exist for + * the feature selector. This may occur when multiple block + * support features use the same custom selector. + */ if ( isset( $style_variation_declarations[ $combined_feature_selectors ] ) ) { $style_variation_declarations[ $combined_feature_selectors ] = array_merge( $style_variation_declarations[ $combined_feature_selectors ], $new_feature_declarations ); } else { @@ -2355,11 +2361,10 @@ static function( $split_feature_selector ) use ( $clean_style_variation_selector } /* - * Remove the feature from the variation's node now the - * styles will be included under the feature level selector. - */ + * Remove the feature from the variation's node now the + * styles will be included under the feature level selector. + */ unset( $style_variation_node[ $feature_name ] ); - } } // Compute declarations for remaining styles not covered by feature level selectors. @@ -2570,13 +2575,13 @@ public function get_root_layout_rules( $selector, $block_metadata ) { * * @since 6.0.0 * - * @param array $data The data to inspect. - * @param bool|array $path Boolean or path to a boolean. - * @param bool $default Default value if the referenced path is missing. - * Default false. + * @param array $data The data to inspect. + * @param bool|array $path Boolean or path to a boolean. + * @param bool $default_value Default value if the referenced path is missing. + * Default false. * @return bool Value of boolean metadata. */ - protected static function get_metadata_boolean( $data, $path, $default = false ) { + protected static function get_metadata_boolean( $data, $path, $default_value = false ) { if ( is_bool( $path ) ) { return $path; } @@ -2588,7 +2593,7 @@ protected static function get_metadata_boolean( $data, $path, $default = false ) } } - return $default; + return $default_value; } /** diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index df3d1ee7308e75..e433a104eac1fa 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -319,10 +319,15 @@ public static function get_theme_data( $deprecated = array(), $options = array() // Classic themes without a theme.json don't support global duotone. $theme_support_data['settings']['color']['defaultDuotone'] = false; + // BEGIN EXPERIMENTAL. // Allow themes to enable appearance tools via theme_support. + // This feature was backported for WordPress 6.2 as of https://core.trac.wordpress.org/ticket/56487 + // and then reverted as of https://core.trac.wordpress.org/ticket/57649 + // Not to backport until the issues are resolved. if ( current_theme_supports( 'appearance-tools' ) ) { $theme_support_data['settings']['appearanceTools'] = true; } + // END EXPERIMENTAL. } $with_theme_supports = new WP_Theme_JSON_Gutenberg( $theme_support_data ); $with_theme_supports->merge( static::$theme ); @@ -566,7 +571,8 @@ public static function get_merged_data( $origin = 'custom' ) { _deprecated_argument( __FUNCTION__, '5.9.0' ); } - $result = static::get_core_data(); + $result = new WP_Theme_JSON(); + $result->merge( static::get_core_data() ); if ( 'default' === $origin ) { $result->set_spacing_sizes(); return $result; diff --git a/lib/compat/wordpress-6.1/blocks.php b/lib/compat/wordpress-6.1/blocks.php index e8093961896a48..8ee0acba6b6cfe 100644 --- a/lib/compat/wordpress-6.1/blocks.php +++ b/lib/compat/wordpress-6.1/blocks.php @@ -137,148 +137,6 @@ function gutenberg_block_type_metadata_multiple_view_scripts( $metadata ) { } add_filter( 'block_type_metadata', 'gutenberg_block_type_metadata_multiple_view_scripts' ); -/** - * Helper function that constructs a WP_Query args array from - * a `Query` block properties. - * - * It's used in QueryLoop, QueryPaginationNumbers and QueryPaginationNext blocks. - * - * `build_query_vars_from_query_block` was introduced in 5.8, for 6.1 we just need - * to update that function and not create a new one. - * - * @param WP_Block $block Block instance. - * @param int $page Current query's page. - * - * @return array Returns the constructed WP_Query arguments. - */ -function gutenberg_build_query_vars_from_query_block( $block, $page ) { - $query = array( - 'post_type' => 'post', - 'order' => 'DESC', - 'orderby' => 'date', - 'post__not_in' => array(), - ); - - if ( isset( $block->context['query'] ) ) { - if ( ! empty( $block->context['query']['postType'] ) ) { - $post_type_param = $block->context['query']['postType']; - if ( is_post_type_viewable( $post_type_param ) ) { - $query['post_type'] = $post_type_param; - } - } - if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) { - $sticky = get_option( 'sticky_posts' ); - if ( 'only' === $block->context['query']['sticky'] ) { - /** - * Passing an empty array to post__in will return have_posts() as true (and all posts will be returned). - * Logic should be used before hand to determine if WP_Query should be used in the event that the array - * being passed to post__in is empty. - * - * @see https://core.trac.wordpress.org/ticket/28099 - */ - $query['post__in'] = ! empty( $sticky ) ? $sticky : array( 0 ); - $query['ignore_sticky_posts'] = 1; - } else { - $query['post__not_in'] = array_merge( $query['post__not_in'], $sticky ); - } - } - if ( ! empty( $block->context['query']['exclude'] ) ) { - $excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] ); - $excluded_post_ids = array_filter( $excluded_post_ids ); - $query['post__not_in'] = array_merge( $query['post__not_in'], $excluded_post_ids ); - } - if ( - isset( $block->context['query']['perPage'] ) && - is_numeric( $block->context['query']['perPage'] ) - ) { - $per_page = absint( $block->context['query']['perPage'] ); - $offset = 0; - - if ( - isset( $block->context['query']['offset'] ) && - is_numeric( $block->context['query']['offset'] ) - ) { - $offset = absint( $block->context['query']['offset'] ); - } - - $query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset; - $query['posts_per_page'] = $per_page; - } - - // We need to migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility. - if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) { - $tax_query = array(); - if ( ! empty( $block->context['query']['categoryIds'] ) ) { - $tax_query[] = array( - 'taxonomy' => 'category', - 'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ), - 'include_children' => false, - ); - } - if ( ! empty( $block->context['query']['tagIds'] ) ) { - $tax_query[] = array( - 'taxonomy' => 'post_tag', - 'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ), - 'include_children' => false, - ); - } - $query['tax_query'] = $tax_query; - } - if ( ! empty( $block->context['query']['taxQuery'] ) ) { - $query['tax_query'] = array(); - foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) { - if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) { - $query['tax_query'][] = array( - 'taxonomy' => $taxonomy, - 'terms' => array_filter( array_map( 'intval', $terms ) ), - 'include_children' => false, - ); - } - } - } - if ( - isset( $block->context['query']['order'] ) && - in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true ) - ) { - $query['order'] = strtoupper( $block->context['query']['order'] ); - } - if ( isset( $block->context['query']['orderBy'] ) ) { - $query['orderby'] = $block->context['query']['orderBy']; - } - if ( ! empty( $block->context['query']['author'] ) ) { - $query['author'] = $block->context['query']['author']; - } - if ( ! empty( $block->context['query']['search'] ) ) { - $query['s'] = $block->context['query']['search']; - } - if ( ! empty( $block->context['query']['parents'] ) && is_post_type_hierarchical( $query['post_type'] ) ) { - $query['post_parent__in'] = array_filter( array_map( 'intval', $block->context['query']['parents'] ) ); - } - } - - /** - * Filters the arguments which will be passed to `WP_Query` for the Query Loop Block. - * - * Anything to this filter should be compatible with the `WP_Query` API to form - * the query context which will be passed down to the Query Loop Block's children. - * This can help, for example, to include additional settings or meta queries not - * directly supported by the core Query Loop Block, and extend its capabilities. - * - * Please note that this will only influence the query that will be rendered on the - * front-end. The editor preview is not affected by this filter. Also, worth noting - * that the editor preview uses the REST API, so, ideally, one should aim to provide - * attributes which are also compatible with the REST API, in order to be able to - * implement identical queries on both sides. - * - * @since 6.1.0 - * - * @param array $query Array containing parameters for `WP_Query` as parsed by the block context. - * @param WP_Block $block Block instance. - * @param int $page Current query's page. - */ - return apply_filters( 'query_loop_block_query_vars', $query, $block, $page ); -} - /** * Register render template for core blocks if handling is missing in WordPress core. * diff --git a/lib/compat/wordpress-6.2/blocks.php b/lib/compat/wordpress-6.2/blocks.php index f432f6a41af8f7..94c6eaabcef9b3 100644 --- a/lib/compat/wordpress-6.2/blocks.php +++ b/lib/compat/wordpress-6.2/blocks.php @@ -26,3 +26,148 @@ function gutenberg_safe_style_attrs_6_2( $attrs ) { return $attrs; } add_filter( 'safe_style_css', 'gutenberg_safe_style_attrs_6_2' ); + +/** + * Helper function that constructs a WP_Query args array from + * a `Query` block properties. + * + * It's used in QueryLoop, QueryPaginationNumbers and QueryPaginationNext blocks. + * + * `build_query_vars_from_query_block` was introduced in 5.8, for 6.1 we just need + * to update that function and not create a new one. + * + * @param WP_Block $block Block instance. + * @param int $page Current query's page. + * + * @return array Returns the constructed WP_Query arguments. + */ +function gutenberg_build_query_vars_from_query_block( $block, $page ) { + $query = array( + 'post_type' => 'post', + 'order' => 'DESC', + 'orderby' => 'date', + 'post__not_in' => array(), + ); + + if ( isset( $block->context['query'] ) ) { + if ( ! empty( $block->context['query']['postType'] ) ) { + $post_type_param = $block->context['query']['postType']; + if ( is_post_type_viewable( $post_type_param ) ) { + $query['post_type'] = $post_type_param; + } + } + if ( isset( $block->context['query']['sticky'] ) && ! empty( $block->context['query']['sticky'] ) ) { + $sticky = get_option( 'sticky_posts' ); + if ( 'only' === $block->context['query']['sticky'] ) { + /** + * Passing an empty array to post__in will return have_posts() as true (and all posts will be returned). + * Logic should be used before hand to determine if WP_Query should be used in the event that the array + * being passed to post__in is empty. + * + * @see https://core.trac.wordpress.org/ticket/28099 + */ + $query['post__in'] = ! empty( $sticky ) ? $sticky : array( 0 ); + $query['ignore_sticky_posts'] = 1; + } else { + $query['post__not_in'] = array_merge( $query['post__not_in'], $sticky ); + } + } + if ( ! empty( $block->context['query']['exclude'] ) ) { + $excluded_post_ids = array_map( 'intval', $block->context['query']['exclude'] ); + $excluded_post_ids = array_filter( $excluded_post_ids ); + $query['post__not_in'] = array_merge( $query['post__not_in'], $excluded_post_ids ); + } + if ( + isset( $block->context['query']['perPage'] ) && + is_numeric( $block->context['query']['perPage'] ) + ) { + $per_page = absint( $block->context['query']['perPage'] ); + $offset = 0; + + if ( + isset( $block->context['query']['offset'] ) && + is_numeric( $block->context['query']['offset'] ) + ) { + $offset = absint( $block->context['query']['offset'] ); + } + + $query['offset'] = ( $per_page * ( $page - 1 ) ) + $offset; + $query['posts_per_page'] = $per_page; + } + + // Migrate `categoryIds` and `tagIds` to `tax_query` for backwards compatibility. + if ( ! empty( $block->context['query']['categoryIds'] ) || ! empty( $block->context['query']['tagIds'] ) ) { + $tax_query = array(); + if ( ! empty( $block->context['query']['categoryIds'] ) ) { + $tax_query[] = array( + 'taxonomy' => 'category', + 'terms' => array_filter( array_map( 'intval', $block->context['query']['categoryIds'] ) ), + 'include_children' => false, + ); + } + if ( ! empty( $block->context['query']['tagIds'] ) ) { + $tax_query[] = array( + 'taxonomy' => 'post_tag', + 'terms' => array_filter( array_map( 'intval', $block->context['query']['tagIds'] ) ), + 'include_children' => false, + ); + } + $query['tax_query'] = $tax_query; + } + if ( ! empty( $block->context['query']['taxQuery'] ) ) { + $query['tax_query'] = array(); + foreach ( $block->context['query']['taxQuery'] as $taxonomy => $terms ) { + if ( is_taxonomy_viewable( $taxonomy ) && ! empty( $terms ) ) { + $query['tax_query'][] = array( + 'taxonomy' => $taxonomy, + 'terms' => array_filter( array_map( 'intval', $terms ) ), + 'include_children' => false, + ); + } + } + } + if ( + isset( $block->context['query']['order'] ) && + in_array( strtoupper( $block->context['query']['order'] ), array( 'ASC', 'DESC' ), true ) + ) { + $query['order'] = strtoupper( $block->context['query']['order'] ); + } + if ( isset( $block->context['query']['orderBy'] ) ) { + $query['orderby'] = $block->context['query']['orderBy']; + } + if ( + isset( $block->context['query']['author'] ) && + (int) $block->context['query']['author'] > 0 + ) { + $query['author'] = (int) $block->context['query']['author']; + } + if ( ! empty( $block->context['query']['search'] ) ) { + $query['s'] = $block->context['query']['search']; + } + if ( ! empty( $block->context['query']['parents'] ) && is_post_type_hierarchical( $query['post_type'] ) ) { + $query['post_parent__in'] = array_filter( array_map( 'intval', $block->context['query']['parents'] ) ); + } + } + + /** + * Filters the arguments which will be passed to `WP_Query` for the Query Loop Block. + * + * Anything to this filter should be compatible with the `WP_Query` API to form + * the query context which will be passed down to the Query Loop Block's children. + * This can help, for example, to include additional settings or meta queries not + * directly supported by the core Query Loop Block, and extend its capabilities. + * + * Please note that this will only influence the query that will be rendered on the + * front-end. The editor preview is not affected by this filter. Also, worth noting + * that the editor preview uses the REST API, so, ideally, one should aim to provide + * attributes which are also compatible with the REST API, in order to be able to + * implement identical queries on both sides. + * + * @since 6.1.0 + * + * @param array $query Array containing parameters for `WP_Query` as parsed by the block context. + * @param WP_Block $block Block instance. + * @param int $page Current query's page. + */ + return apply_filters( 'query_loop_block_query_vars', $query, $block, $page ); +} diff --git a/package-lock.json b/package-lock.json index 5f8c1da350247d..7830f3c448a9dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "15.2.2", + "version": "15.3.0-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 0bcf8ca16be47e..dc54d7361fadd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "15.2.2", + "version": "15.3.0-rc.1", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", @@ -299,11 +299,12 @@ "test": "npm-run-all lint test:unit", "test:create-block": "bash ./bin/test-create-block.sh", "test:e2e": "wp-scripts test-e2e --config packages/e2e-tests/jest.config.js", - "test:e2e:debug": "wp-scripts --inspect-brk test-e2e --config packages/e2e-tests/jest.config.js --puppeteer-devtools", + "test:e2e:debug": "wp-scripts --inspect-brk test-e2e --runInBand --no-cache --verbose --config packages/e2e-tests/jest.config.js --puppeteer-devtools", "test:e2e:playwright": "playwright test --config test/e2e/playwright.config.ts", "test:e2e:storybook": "playwright test --config test/storybook-playwright/playwright.config.ts", "test:e2e:watch": "npm run test:e2e -- --watch", "test:performance": "wp-scripts test-e2e --config packages/e2e-tests/jest.performance.config.js", + "test:performance:debug": "wp-scripts --inspect-brk test-e2e --runInBand --no-cache --verbose --config packages/e2e-tests/jest.performance.config.js --puppeteer-devtools", "test:php": "npm-run-all lint:php test:unit:php", "test:php:watch": "wp-env run composer run-script test:watch", "test:unit": "wp-scripts test-unit-js --config test/unit/jest.config.js", diff --git a/packages/block-editor/src/components/block-lock/modal.js b/packages/block-editor/src/components/block-lock/modal.js index 580498bc38e382..cfafa6c031bbd1 100644 --- a/packages/block-editor/src/components/block-lock/modal.js +++ b/packages/block-editor/src/components/block-lock/modal.js @@ -187,6 +187,7 @@ export default function BlockLockModal( { clientId, onClose } ) { { hasTemplateLock && ( { const blockToSelect = - previousBlockClientId || nextBlockClientId; + previousBlockClientId || + nextBlockClientId || + firstParentClientId; if ( blockToSelect && @@ -166,6 +168,7 @@ export function BlockSettingsDropdown( { __experimentalSelectBlock, previousBlockClientId, nextBlockClientId, + firstParentClientId, selectedBlockClientIds, ] ); diff --git a/packages/block-editor/src/components/date-format-picker/index.js b/packages/block-editor/src/components/date-format-picker/index.js index 6bda9f933e366d..8ee6fcd5bde44f 100644 --- a/packages/block-editor/src/components/date-format-picker/index.js +++ b/packages/block-editor/src/components/date-format-picker/index.js @@ -50,14 +50,12 @@ export default function DateFormatPicker( {
{ __( 'Date format' ) } - { __( 'Default format' ) } - - { dateI18n( defaultFormat, EXAMPLE_DATE ) } - - - } + __nextHasNoMarginBottom + label={ __( 'Default format' ) } + help={ `${ __( 'Example:' ) } ${ dateI18n( + defaultFormat, + EXAMPLE_DATE + ) }` } checked={ ! format } onChange={ ( checked ) => onChange( checked ? null : defaultFormat ) diff --git a/packages/block-editor/src/components/date-format-picker/style.scss b/packages/block-editor/src/components/date-format-picker/style.scss index f8d82c591cc0ae..d1ad52408d3502 100644 --- a/packages/block-editor/src/components/date-format-picker/style.scss +++ b/packages/block-editor/src/components/date-format-picker/style.scss @@ -2,11 +2,6 @@ margin-bottom: $grid-unit-20; } -.block-editor-date-format-picker__default-format-toggle-control__hint { - color: $gray-700; - display: block; -} - .block-editor-date-format-picker__custom-format-select-control__custom-option { border-top: 1px solid $gray-300; diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js index 33557025b84922..f483a9208e6e11 100644 --- a/packages/block-editor/src/components/global-styles/hooks.js +++ b/packages/block-editor/src/components/global-styles/hooks.js @@ -55,7 +55,7 @@ const VALID_SETTINGS = [ 'spacing.margin', 'spacing.padding', 'spacing.units', - 'typography.fuild', + 'typography.fluid', 'typography.customFontSize', 'typography.dropCap', 'typography.fontFamilies', diff --git a/packages/block-editor/src/components/link-control/settings.js b/packages/block-editor/src/components/link-control/settings.js index beddc00d5cd50f..4e71bccaf3286d 100644 --- a/packages/block-editor/src/components/link-control/settings.js +++ b/packages/block-editor/src/components/link-control/settings.js @@ -20,6 +20,7 @@ const LinkControlSettings = ( { value, onChange = noop, settings } ) => { const theSettings = settings.map( ( setting ) => ( { onClose={ close } renderSettings={ () => ( diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index a2adb62f26b5c0..b56d9f64b4e832 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -80,7 +80,7 @@ export function getColorsFromDuotonePreset( duotone, duotonePalette ) { return; } const preset = duotonePalette?.find( ( { slug } ) => { - return slug === duotone; + return duotone === `var:preset|duotone|${ slug }`; } ); return preset ? preset.colors : undefined; @@ -97,7 +97,7 @@ export function getDuotonePresetFromColors( colors, duotonePalette ) { ); } ); - return preset ? preset.slug : undefined; + return preset ? `var:preset|duotone|${ preset.slug }` : undefined; } function DuotonePanel( { attributes, setAttributes } ) { diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 836a9cd5a57c6e..aa2d278aee5826 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -211,6 +211,7 @@ function LayoutPanel( { { showInheritToggle && ( <> { it( 'should return undefined if a non-existent preset is provided', () => { expect( - getColorsFromDuotonePreset( 'does-not-exist', duotonePalette ) + getColorsFromDuotonePreset( + `var:preset|duotone|does-not-exist`, + duotonePalette + ) ).toBeUndefined(); } ); it( 'should return the colors from the preset if found', () => { expect( getColorsFromDuotonePreset( - duotonePalette[ 2 ].slug, + `var:preset|duotone|${ duotonePalette[ 2 ].slug }`, duotonePalette ) ).toEqual( duotonePalette[ 2 ].colors ); @@ -93,7 +96,7 @@ describe( 'Duotone utilities', () => { duotonePalette[ 2 ].colors, duotonePalette ) - ).toEqual( duotonePalette[ 2 ].slug ); + ).toEqual( `var:preset|duotone|${ duotonePalette[ 2 ].slug }` ); } ); } ); } ); diff --git a/packages/block-editor/src/layouts/flex.js b/packages/block-editor/src/layouts/flex.js index f940e3f971da8c..fd6f377ea591d1 100644 --- a/packages/block-editor/src/layouts/flex.js +++ b/packages/block-editor/src/layouts/flex.js @@ -347,6 +347,7 @@ function FlexWrapControl( { layout, onChange } ) { const { flexWrap = 'wrap' } = layout; return ( { onChange( { diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 98bdf1bffc78c0..b3f437fc94accf 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -232,17 +232,24 @@ export function selectBlock( clientId, initialPosition = 0 ) { /** * Yields action objects used in signalling that the block preceding the given - * clientId should be selected. + * clientId (or optionally, its first parent from bottom to top) + * should be selected. * - * @param {string} clientId Block client ID. + * @param {string} clientId Block client ID. + * @param {boolean} orFirstParent If true, select the first parent if there is no previous block. */ export const selectPreviousBlock = - ( clientId ) => + ( clientId, orFirstParent = false ) => ( { select, dispatch } ) => { const previousBlockClientId = select.getPreviousBlockClientId( clientId ); if ( previousBlockClientId ) { dispatch.selectBlock( previousBlockClientId, -1 ); + } else if ( orFirstParent ) { + const firstParentClientId = select.getBlockRootClientId( clientId ); + if ( firstParentClientId ) { + dispatch.selectBlock( firstParentClientId, -1 ); + } } }; @@ -1197,7 +1204,8 @@ export const removeBlocks = } if ( selectPrevious ) { - dispatch.selectPreviousBlock( clientIds[ 0 ] ); + const shouldSelectParent = true; + dispatch.selectPreviousBlock( clientIds[ 0 ], shouldSelectParent ); } dispatch( { type: 'REMOVE_BLOCKS', clientIds } ); diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index 45e432ad8bf3f8..65a1eca96d5fe3 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -625,7 +625,8 @@ describe( 'actions', () => { removeBlocks( clientIds )( { select, dispatch } ); expect( dispatch.selectPreviousBlock ).toHaveBeenCalledWith( - clientId + clientId, + true ); expect( dispatch ).toHaveBeenCalledWith( { @@ -734,7 +735,8 @@ describe( 'actions', () => { removeBlock( clientId )( { select, dispatch } ); expect( dispatch.selectPreviousBlock ).toHaveBeenCalledWith( - clientId + clientId, + true ); expect( dispatch ).toHaveBeenCalledWith( { diff --git a/packages/block-library/src/archives/edit.js b/packages/block-library/src/archives/edit.js index 9f1f681e95a09b..4eed206314e427 100644 --- a/packages/block-library/src/archives/edit.js +++ b/packages/block-library/src/archives/edit.js @@ -19,6 +19,7 @@ export default function ArchivesEdit( { attributes, setAttributes } ) { @@ -29,6 +30,7 @@ export default function ArchivesEdit( { attributes, setAttributes } ) { /> { displayAsDropdown && ( @@ -39,6 +41,7 @@ export default function ArchivesEdit( { attributes, setAttributes } ) { /> ) } diff --git a/packages/block-library/src/audio/edit.js b/packages/block-library/src/audio/edit.js index a11ab6fdc9442a..57bcb089c664fb 100644 --- a/packages/block-library/src/audio/edit.js +++ b/packages/block-library/src/audio/edit.js @@ -207,12 +207,14 @@ function AudioEdit( { setAttributes( { isLink: ! attributes.isLink } ) diff --git a/packages/block-library/src/categories/edit.js b/packages/block-library/src/categories/edit.js index ef47a5c91d8ff8..15a526f249cf22 100644 --- a/packages/block-library/src/categories/edit.js +++ b/packages/block-library/src/categories/edit.js @@ -143,27 +143,32 @@ export default function CategoriesEdit( { { ! showOnlyTopLevel && ( ) } diff --git a/packages/block-library/src/comment-author-name/edit.js b/packages/block-library/src/comment-author-name/edit.js index e71d26ccee361d..82e0c6b100ce77 100644 --- a/packages/block-library/src/comment-author-name/edit.js +++ b/packages/block-library/src/comment-author-name/edit.js @@ -72,12 +72,14 @@ export default function Edit( { setAttributes( { isLink: ! isLink } ) } checked={ isLink } /> { isLink && ( setAttributes( { diff --git a/packages/block-library/src/comment-date/edit.js b/packages/block-library/src/comment-date/edit.js index 4127f169c2db45..7139abece96397 100644 --- a/packages/block-library/src/comment-date/edit.js +++ b/packages/block-library/src/comment-date/edit.js @@ -48,6 +48,7 @@ export default function Edit( { } /> setAttributes( { isLink: ! isLink } ) } checked={ isLink } diff --git a/packages/block-library/src/comment-edit-link/edit.js b/packages/block-library/src/comment-edit-link/edit.js index 3f45948bc5c060..c2360063e33ad9 100644 --- a/packages/block-library/src/comment-edit-link/edit.js +++ b/packages/block-library/src/comment-edit-link/edit.js @@ -39,6 +39,7 @@ export default function Edit( { setAttributes( { diff --git a/packages/block-library/src/comments-title/edit.js b/packages/block-library/src/comments-title/edit.js index 7f4660d5735b27..72e16c88346831 100644 --- a/packages/block-library/src/comments-title/edit.js +++ b/packages/block-library/src/comments-title/edit.js @@ -110,6 +110,7 @@ export default function Edit( { @@ -117,6 +118,7 @@ export default function Edit( { } /> diff --git a/packages/block-library/src/cover/edit/inspector-controls.js b/packages/block-library/src/cover/edit/inspector-controls.js index b07f257ebf903d..fcacf44e1c6851 100644 --- a/packages/block-library/src/cover/edit/inspector-controls.js +++ b/packages/block-library/src/cover/edit/inspector-controls.js @@ -171,12 +171,14 @@ export default function CoverInspectorControls( { { isImageBackground && ( <> ) } { hasLinkTo && ( ) } @@ -48,6 +49,7 @@ export default function LatestComments( { attributes, setAttributes } ) { } /> @@ -55,6 +57,7 @@ export default function LatestComments( { attributes, setAttributes } ) { } /> diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index cc8a044b0a1eab..bbb03035723d77 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -247,6 +247,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { @@ -254,6 +255,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { } /> @@ -264,6 +266,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { @@ -315,6 +318,7 @@ export default function LatestPostsEdit( { attributes, setAttributes } ) { /> diff --git a/packages/block-library/src/list-item/edit.js b/packages/block-library/src/list-item/edit.js index 738a8ab397adf3..3f26840ad345f9 100644 --- a/packages/block-library/src/list-item/edit.js +++ b/packages/block-library/src/list-item/edit.js @@ -88,9 +88,16 @@ export default function ListItemEdit( { placeholder={ placeholder || __( 'List' ) } onSplit={ onSplit } onMerge={ onMerge } - onReplace={ ( blocks, ...args ) => { - onReplace( convertToListItems( blocks ), ...args ); - } } + onReplace={ + onReplace + ? ( blocks, ...args ) => { + onReplace( + convertToListItems( blocks ), + ...args + ); + } + : undefined + } /> { innerBlocksProps.children } diff --git a/packages/block-library/src/list-item/hooks/use-outdent-list-item.js b/packages/block-library/src/list-item/hooks/use-outdent-list-item.js index 870aeb5088f805..3f566dac30ab5a 100644 --- a/packages/block-library/src/list-item/hooks/use-outdent-list-item.js +++ b/packages/block-library/src/list-item/hooks/use-outdent-list-item.js @@ -112,7 +112,8 @@ export default function useOutdentListItem( clientId ) { getBlockIndex( parentListItemId ) + 1 ); if ( ! getBlockOrder( parentListId ).length ) { - removeBlock( parentListId ); + const shouldSelectParent = false; + removeBlock( parentListId, shouldSelectParent ); } } ); }, [] ), diff --git a/packages/block-library/src/list/ordered-list-settings.js b/packages/block-library/src/list/ordered-list-settings.js index cbcd954a4682ff..7fd51a5c7e576e 100644 --- a/packages/block-library/src/list/ordered-list-settings.js +++ b/packages/block-library/src/list/ordered-list-settings.js @@ -25,6 +25,7 @@ const OrderedListSettings = ( { setAttributes, reversed, start } ) => ( step="1" /> { diff --git a/packages/block-library/src/loginout/edit.js b/packages/block-library/src/loginout/edit.js index 64292a167e6a90..b6c2e9cf013041 100644 --- a/packages/block-library/src/loginout/edit.js +++ b/packages/block-library/src/loginout/edit.js @@ -13,6 +13,7 @@ export default function LoginOutEdit( { attributes, setAttributes } ) { @@ -22,6 +23,7 @@ export default function LoginOutEdit( { attributes, setAttributes } ) { } /> diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js index 7e09bfa3193ccc..a2eecf255cad7a 100644 --- a/packages/block-library/src/media-text/edit.js +++ b/packages/block-library/src/media-text/edit.js @@ -243,6 +243,7 @@ function MediaTextEdit( { attributes, isSelected, setAttributes, clientId } ) { const mediaTextGeneralSettings = ( @@ -253,6 +254,7 @@ function MediaTextEdit( { attributes, isSelected, setAttributes, clientId } ) { /> { mediaType === 'image' && ( diff --git a/packages/block-library/src/more/edit.js b/packages/block-library/src/more/edit.js index f0ed625e458d70..7757af5e96ba1c 100644 --- a/packages/block-library/src/more/edit.js +++ b/packages/block-library/src/more/edit.js @@ -43,6 +43,7 @@ export default function MoreEdit( { { if ( ref || ! hasResolvedNavigationMenus || isConvertingClassicMenu || - fallbackNavigationMenus?.length > 0 + fallbackNavigationMenus?.length > 0 || + hasUnsavedBlocks ) { return; } @@ -323,13 +335,14 @@ function Navigation( { if ( getBlockType( 'core/page-list' ) ) { defaultBlocks = [ createBlock( 'core/page-list' ) ]; } + createNavigationMenu( 'Navigation', // TODO - use the template slug in future defaultBlocks, 'publish' ); } - }, [ hasResolvedNavigationMenus ] ); + }, [ hasResolvedNavigationMenus, hasUnsavedBlocks ] ); const navRef = useRef(); @@ -349,9 +362,6 @@ function Navigation( { classicMenus?.length === 0 && ! hasUncontrolledInnerBlocks; - const isEntityAvailable = - ! isNavigationMenuMissing && isNavigationMenuResolved; - // "loading" state: // - there is a menu creation process in progress. // - there is a classic menu conversion process in progress. @@ -642,6 +652,7 @@ function Navigation( { <>

{ __( 'Submenus' ) }

{ setAttributes( { @@ -655,6 +666,7 @@ function Navigation( { /> { setAttributes( { @@ -726,14 +738,6 @@ function Navigation( { ); - // If the block has inner blocks, but no menu id, then these blocks are either: - // - inserted via a pattern. - // - inserted directly via Code View (or otherwise). - // - from an older version of navigation block added before the block used a wp_navigation entity. - // Consider this state as 'unsaved' and offer an uncontrolled version of inner blocks, - // that automatically saves the menu as an entity when changes are made to the inner blocks. - const hasUnsavedBlocks = hasUncontrolledInnerBlocks && ! isEntityAvailable; - const isManageMenusButtonDisabled = ! hasManagePermissions || ! hasResolvedNavigationMenus; diff --git a/packages/block-library/src/navigation/edit/overlay-menu-preview.js b/packages/block-library/src/navigation/edit/overlay-menu-preview.js index aa0abd11ad2757..adb1377a604ede 100644 --- a/packages/block-library/src/navigation/edit/overlay-menu-preview.js +++ b/packages/block-library/src/navigation/edit/overlay-menu-preview.js @@ -17,6 +17,7 @@ export default function OverlayMenuPreview( { setAttributes, hasIcon, icon } ) { return ( <> diff --git a/packages/block-library/src/post-author-name/edit.js b/packages/block-library/src/post-author-name/edit.js index 8eda3bf92d193c..14437972062315 100644 --- a/packages/block-library/src/post-author-name/edit.js +++ b/packages/block-library/src/post-author-name/edit.js @@ -71,12 +71,14 @@ function PostAuthorNameEdit( { setAttributes( { isLink: ! isLink } ) } checked={ isLink } /> { isLink && ( setAttributes( { diff --git a/packages/block-library/src/post-author/edit.js b/packages/block-library/src/post-author/edit.js index 9573eedde66cfe..a9b7106f49d388 100644 --- a/packages/block-library/src/post-author/edit.js +++ b/packages/block-library/src/post-author/edit.js @@ -120,6 +120,7 @@ function PostAuthorEdit( { /> ) ) } @@ -140,6 +141,7 @@ function PostAuthorEdit( { /> ) } @@ -147,12 +149,14 @@ function PostAuthorEdit( { } /> setAttributes( { isLink: ! isLink } ) } /> { isLink && ( setAttributes( { diff --git a/packages/block-library/src/post-date/edit.js b/packages/block-library/src/post-date/edit.js index 810eea2ebc9051..6ce9144023e6e7 100644 --- a/packages/block-library/src/post-date/edit.js +++ b/packages/block-library/src/post-date/edit.js @@ -150,6 +150,7 @@ export default function PostDateEdit( { } /> setAttributes( { diff --git a/packages/block-library/src/post-excerpt/edit.js b/packages/block-library/src/post-excerpt/edit.js index abe03705db6805..378aa34cd00baa 100644 --- a/packages/block-library/src/post-excerpt/edit.js +++ b/packages/block-library/src/post-excerpt/edit.js @@ -194,6 +194,7 @@ export default function PostExcerptEditor( { diff --git a/packages/block-library/src/post-featured-image/edit.js b/packages/block-library/src/post-featured-image/edit.js index f378a14f5c9d1b..950dbd57667b3d 100644 --- a/packages/block-library/src/post-featured-image/edit.js +++ b/packages/block-library/src/post-featured-image/edit.js @@ -141,6 +141,7 @@ export default function PostFeaturedImageEdit( { setAttributes( { diff --git a/packages/block-library/src/post-navigation-link/edit.js b/packages/block-library/src/post-navigation-link/edit.js index 0bdb864768b432..f899788428bcdb 100644 --- a/packages/block-library/src/post-navigation-link/edit.js +++ b/packages/block-library/src/post-navigation-link/edit.js @@ -52,6 +52,7 @@ export default function PostNavigationLinkEdit( { { showTitle && ( setAttributes( { isLink: ! isLink } ) } checked={ isLink } @@ -125,6 +126,7 @@ export default function PostTitleEdit( { { isLink && ( <> setAttributes( { diff --git a/packages/block-library/src/query-title/edit.js b/packages/block-library/src/query-title/edit.js index 7c9689f3367408..da321bead7c0b8 100644 --- a/packages/block-library/src/query-title/edit.js +++ b/packages/block-library/src/query-title/edit.js @@ -49,6 +49,7 @@ export default function QueryTitleEdit( { setAttributes( { showPrefix: ! showPrefix } ) @@ -72,6 +73,7 @@ export default function QueryTitleEdit( { setAttributes( { diff --git a/packages/block-library/src/query/edit/inspector-controls/index.js b/packages/block-library/src/query/edit/inspector-controls/index.js index 9d99e5ad793011..95ccb43e58452a 100644 --- a/packages/block-library/src/query/edit/inspector-controls/index.js +++ b/packages/block-library/src/query/edit/inspector-controls/index.js @@ -132,6 +132,7 @@ export default function QueryInspectorControls( { { showInheritControl && ( setAttributes( { diff --git a/packages/block-library/src/rss/edit.js b/packages/block-library/src/rss/edit.js index 0cf252e038b6f9..2fd94cdd2a021a 100644 --- a/packages/block-library/src/rss/edit.js +++ b/packages/block-library/src/rss/edit.js @@ -126,16 +126,19 @@ export default function RSSEdit( { attributes, setAttributes } ) { required /> setAttributes( { isLink: ! isLink } ) } checked={ isLink } @@ -307,6 +308,7 @@ const SiteLogo = ( { { isLink && ( <> setAttributes( { @@ -320,6 +322,7 @@ const SiteLogo = ( { { canUserEdit && ( <> { setAttributes( { shouldSyncIcon: value } ); diff --git a/packages/block-library/src/site-title/edit/index.js b/packages/block-library/src/site-title/edit/index.js index 5980dff6d88193..067f4fc7392036 100644 --- a/packages/block-library/src/site-title/edit/index.js +++ b/packages/block-library/src/site-title/edit/index.js @@ -111,12 +111,14 @@ export default function SiteTitleEdit( { setAttributes( { isLink: ! isLink } ) } checked={ isLink } /> { isLink && ( setAttributes( { diff --git a/packages/block-library/src/social-links/edit.js b/packages/block-library/src/social-links/edit.js index 383ae7b6f1355f..084ab1df9ac380 100644 --- a/packages/block-library/src/social-links/edit.js +++ b/packages/block-library/src/social-links/edit.js @@ -197,6 +197,7 @@ export function SocialLinksEdit( props ) { @@ -204,6 +205,7 @@ export function SocialLinksEdit( props ) { } /> diff --git a/packages/block-library/src/table-of-contents/edit.js b/packages/block-library/src/table-of-contents/edit.js index 38e93e06d7830b..7f3bb6529bf323 100644 --- a/packages/block-library/src/table-of-contents/edit.js +++ b/packages/block-library/src/table-of-contents/edit.js @@ -251,6 +251,7 @@ export default function TableOfContentsEdit( { diff --git a/packages/block-library/src/table/edit.js b/packages/block-library/src/table/edit.js index 904cc3028fc715..4f186f77a72918 100644 --- a/packages/block-library/src/table/edit.js +++ b/packages/block-library/src/table/edit.js @@ -478,6 +478,7 @@ function TableEdit( { className="blocks-table-settings" > diff --git a/packages/block-library/src/video/edit-common-settings.js b/packages/block-library/src/video/edit-common-settings.js index 5c8f5e2ab531f0..f0cdf210fe0d5f 100644 --- a/packages/block-library/src/video/edit-common-settings.js +++ b/packages/block-library/src/video/edit-common-settings.js @@ -48,27 +48,32 @@ const VideoSettings = ( { setAttributes, attributes } ) => { return ( <> 2 ) { + history.current.delete( Array.from( history.current )[ 0 ] ); + } + + return Array.from( history.current )[ 0 ]; +} + export function useAutocompleteProps( options ) { - const [ isVisible, setIsVisible ] = useState( false ); const ref = useRef(); - const recordAfterInput = useRef(); const onKeyDownRef = useRef(); + const { record } = options; + const previousRecord = useLastDifferentValue( record ); const { popover, listBoxId, activeId, onKeyDown } = useAutocomplete( { ...options, contentRef: ref, } ); onKeyDownRef.current = onKeyDown; - useEffect( () => { - if ( isVisible ) { - if ( ! recordAfterInput.current ) { - recordAfterInput.current = options.record; - } else if ( - recordAfterInput.current.start !== options.record.start || - recordAfterInput.current.end !== options.record.end - ) { - setIsVisible( false ); - recordAfterInput.current = null; - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ options.record ] ); - const mergedRefs = useMergeRefs( [ ref, useRefEffect( ( element ) => { function _onKeyDown( event ) { onKeyDownRef.current( event ); } - function _onInput() { - // Only show auto complete UI if the user is inputting text. - setIsVisible( true ); - recordAfterInput.current = null; - } element.addEventListener( 'keydown', _onKeyDown ); - element.addEventListener( 'input', _onInput ); return () => { element.removeEventListener( 'keydown', _onKeyDown ); - element.removeEventListener( 'input', _onInput ); }; }, [] ), ] ); - if ( ! isVisible ) { + // We only want to show the popover if the user has typed something. + const didUserInput = record.text !== previousRecord?.text; + + if ( ! didUserInput ) { return { ref: mergedRefs }; } diff --git a/packages/e2e-tests/jest.config.js b/packages/e2e-tests/jest.config.js index 5f9d5b75b24ef7..0c88768f0f58aa 100644 --- a/packages/e2e-tests/jest.config.js +++ b/packages/e2e-tests/jest.config.js @@ -18,6 +18,10 @@ module.exports = { '/node_modules/', 'e2e-tests/specs/performance/', ], + snapshotFormat: { + escapeString: false, + printBasicPrototype: false, + }, reporters: [ ...baseConfig.reporters, // Report flaky tests results into artifacts for used in `report-flaky-tests` action. diff --git a/packages/e2e-tests/specs/editor/blocks/pullquote.test.js b/packages/e2e-tests/specs/editor/blocks/pullquote.test.js index 9c9b9f4513fae6..13d6431c6dbcb4 100644 --- a/packages/e2e-tests/specs/editor/blocks/pullquote.test.js +++ b/packages/e2e-tests/specs/editor/blocks/pullquote.test.js @@ -20,7 +20,7 @@ describe( 'Quote', () => { expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` " -
+

test

" @@ -30,7 +30,7 @@ describe( 'Quote', () => { expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` " -

test

+

test

" ` ); @@ -38,7 +38,7 @@ describe( 'Quote', () => { expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` " -
+

test

" diff --git a/packages/e2e-tests/specs/editor/plugins/__snapshots__/align-hook.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/align-hook.test.js.snap index e0075595e01b08..6c04d30c41be9d 100644 --- a/packages/e2e-tests/specs/editor/plugins/__snapshots__/align-hook.test.js.snap +++ b/packages/e2e-tests/specs/editor/plugins/__snapshots__/align-hook.test.js.snap @@ -1,43 +1,43 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Align Hook Works As Expected Block with align array Correctly applies the selected alignment and correctly removes the alignment 1`] = ` -" -
Test Align Hook
+" +
Test Align Hook
" `; exports[`Align Hook Works As Expected Block with align array Correctly applies the selected alignment and correctly removes the alignment 2`] = ` " -
Test Align Hook
+
Test Align Hook
" `; exports[`Align Hook Works As Expected Block with align true Correctly applies the selected alignment and correctly removes the alignment 1`] = ` -" -
Test Align Hook
+" +
Test Align Hook
" `; exports[`Align Hook Works As Expected Block with align true Correctly applies the selected alignment and correctly removes the alignment 2`] = ` " -
Test Align Hook
+
Test Align Hook
" `; exports[`Align Hook Works As Expected Block with default align Correctly applies the selected alignment and correctly removes the alignment 1`] = ` -" -
Test Align Hook
+" +
Test Align Hook
" `; exports[`Align Hook Works As Expected Block with default align Correctly applies the selected alignment and correctly removes the alignment 2`] = ` -" -
Test Align Hook
+" +
Test Align Hook
" `; exports[`Align Hook Works As Expected Block with no alignment set Does not save any alignment related attribute or class 1`] = ` " -
Test Align Hook
+
Test Align Hook
" `; diff --git a/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap index 5eec9e79ced1f6..fb3f84595e5e2b 100644 --- a/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap +++ b/packages/e2e-tests/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap @@ -3,23 +3,23 @@ exports[`Container block without paragraph support ensures we can use the alternative block appender properly 1`] = ` " -
\\"\\"/
+
" `; exports[`InnerBlocks Template Sync Ensure inner block writing flow works as expected without additional paragraphs added 1`] = ` " - -

Test Paragraph

+ +

Test Paragraph

" `; exports[`InnerBlocks Template Sync Ensures blocks without locking are kept intact even if they do not match the template 1`] = ` " - -

Content…

+ +

Content…

@@ -30,28 +30,28 @@ exports[`InnerBlocks Template Sync Ensures blocks without locking are kept intac exports[`InnerBlocks Template Sync Removes blocks that are not expected by the template if a lock all exists 1`] = ` " - -

Content…

+ +

Content…

" `; exports[`InnerBlocks Template Sync Synchronizes blocks if lock 'all' is set and the template prop is changed 1`] = ` " - -

Content…

+ +

Content…

" `; exports[`InnerBlocks Template Sync Synchronizes blocks if lock 'all' is set and the template prop is changed 2`] = ` -" - -

Content…

+" + +

Content…

- -

Two

+ +

Two

" `; diff --git a/packages/e2e-tests/specs/editor/plugins/__snapshots__/cpt-locking.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/cpt-locking.test.js.snap index 0acc31727beeb5..5c6867e75ad7af 100644 --- a/packages/e2e-tests/specs/editor/plugins/__snapshots__/cpt-locking.test.js.snap +++ b/packages/e2e-tests/specs/editor/plugins/__snapshots__/cpt-locking.test.js.snap @@ -2,52 +2,52 @@ exports[`cpt locking template_lock all should insert line breaks when using enter and shift-enter 1`] = ` " -
\\"\\"/
+
- +

First line
Second line
Third line

-
+

-
+
" `; exports[`cpt locking template_lock all should not error when deleting the cotents of a paragraph 1`] = ` " -
\\"\\"/
+
- +

-
+

-
+
" `; exports[`cpt locking template_lock all unlocked group should allow blocks to be moved 1`] = ` -" -
+" +

p1

-
+

@@ -55,9 +55,9 @@ exports[`cpt locking template_lock all unlocked group should allow blocks to be `; exports[`cpt locking template_lock all unlocked group should allow blocks to be removed 1`] = ` -" -
-
+" +
+

@@ -66,21 +66,21 @@ exports[`cpt locking template_lock all unlocked group should allow blocks to be exports[`cpt locking template_lock false should allow blocks to be inserted 1`] = ` " -
\\"\\"/
+
- +

-
+

-
+
@@ -91,57 +91,57 @@ exports[`cpt locking template_lock false should allow blocks to be inserted 1`] `; exports[`cpt locking template_lock false should allow blocks to be moved 1`] = ` -" +"

p1

-
\\"\\"/
+
-
+

-
+
" `; exports[`cpt locking template_lock false should allow blocks to be removed 1`] = ` " -
\\"\\"/
+
-
+

-
+
" `; exports[`cpt locking template_lock insert should allow blocks to be moved 1`] = ` -" +"

p1

-
\\"\\"/
+
-
+

-
+
" `; diff --git a/packages/e2e-tests/specs/editor/plugins/__snapshots__/iframed-inline-styles.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/iframed-inline-styles.test.js.snap index 46d3e807bff832..49823edc5b1ea5 100644 --- a/packages/e2e-tests/specs/editor/plugins/__snapshots__/iframed-inline-styles.test.js.snap +++ b/packages/e2e-tests/specs/editor/plugins/__snapshots__/iframed-inline-styles.test.js.snap @@ -2,6 +2,6 @@ exports[`iframed inline styles should load inline styles in iframe 1`] = ` " -
Save
+
Save
" `; diff --git a/packages/e2e-tests/specs/editor/plugins/__snapshots__/iframed-masonry-block.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/iframed-masonry-block.test.js.snap index 0033b3aff44d12..a37b33cdc3f410 100644 --- a/packages/e2e-tests/specs/editor/plugins/__snapshots__/iframed-masonry-block.test.js.snap +++ b/packages/e2e-tests/specs/editor/plugins/__snapshots__/iframed-masonry-block.test.js.snap @@ -2,6 +2,6 @@ exports[`iframed masonry block should load script and dependencies in iframe 1`] = ` " -
+
" `; diff --git a/packages/e2e-tests/specs/editor/plugins/__snapshots__/inner-blocks-render-appender.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/inner-blocks-render-appender.test.js.snap index 480121a3ceb8f1..232e6e02b398eb 100644 --- a/packages/e2e-tests/specs/editor/plugins/__snapshots__/inner-blocks-render-appender.test.js.snap +++ b/packages/e2e-tests/specs/editor/plugins/__snapshots__/inner-blocks-render-appender.test.js.snap @@ -2,8 +2,8 @@ exports[`RenderAppender prop of InnerBlocks Users can customize the appender and can still insert blocks using exposed components 1`] = ` " -
-
+
+

@@ -12,14 +12,14 @@ exports[`RenderAppender prop of InnerBlocks Users can customize the appender and exports[`RenderAppender prop of InnerBlocks Users can dynamically customize the appender 1`] = ` " -
-
+
+

-
+
" `; diff --git a/packages/e2e-tests/specs/editor/plugins/__snapshots__/plugins-api.test.js.snap b/packages/e2e-tests/specs/editor/plugins/__snapshots__/plugins-api.test.js.snap index e2d70612669aef..34c57bf11e7bc5 100644 --- a/packages/e2e-tests/specs/editor/plugins/__snapshots__/plugins-api.test.js.snap +++ b/packages/e2e-tests/specs/editor/plugins/__snapshots__/plugins-api.test.js.snap @@ -2,6 +2,6 @@ exports[`Using Plugins API Document Setting Custom Panel Should render a custom panel inside Document Setting sidebar 1`] = `"My Custom Panel"`; -exports[`Using Plugins API Sidebar Medium screen Should open plugins sidebar using More Menu item and render content 1`] = `"
(no title)
Plugin sidebar title
"`; +exports[`Using Plugins API Sidebar Medium screen Should open plugins sidebar using More Menu item and render content 1`] = `"
(no title)
Plugin sidebar title
"`; -exports[`Using Plugins API Sidebar Should open plugins sidebar using More Menu item and render content 1`] = `"
(no title)
Plugin sidebar title
"`; +exports[`Using Plugins API Sidebar Should open plugins sidebar using More Menu item and render content 1`] = `"
(no title)
Plugin sidebar title
"`; diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/adding-patterns.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/adding-patterns.test.js.snap index 3b15d7589b63a6..0daa12f8a8660d 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/adding-patterns.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/adding-patterns.test.js.snap @@ -1,11 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`adding patterns should insert a block pattern 1`] = ` -" - " `; diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/block-grouping.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/block-grouping.test.js.snap index 39621047016d4d..5ffca68763f42e 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/block-grouping.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/block-grouping.test.js.snap @@ -1,13 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Block Grouping Group creation creates a group from multiple blocks of different types via block transforms 1`] = ` -" -
-

Group Heading

+" +
+

Group Heading

-
\\"\\"/
+
@@ -17,8 +17,8 @@ exports[`Block Grouping Group creation creates a group from multiple blocks of d `; exports[`Block Grouping Group creation creates a group from multiple blocks of the same type via block transforms 1`] = ` -" -
+" +

First Paragraph

@@ -33,8 +33,8 @@ exports[`Block Grouping Group creation creates a group from multiple blocks of t `; exports[`Block Grouping Group creation creates a group from multiple blocks of the same type via options toolbar 1`] = ` -" -
+" +

First Paragraph

@@ -49,13 +49,13 @@ exports[`Block Grouping Group creation creates a group from multiple blocks of t `; exports[`Block Grouping Group creation groups and ungroups multiple blocks of different types via options toolbar 1`] = ` -" -
-

Group Heading

+" +
+

Group Heading

-
\\"\\"/
+
@@ -66,11 +66,11 @@ exports[`Block Grouping Group creation groups and ungroups multiple blocks of di exports[`Block Grouping Group creation groups and ungroups multiple blocks of different types via options toolbar 2`] = ` " -

Group Heading

+

Group Heading

-
\\"\\"/
+
@@ -79,17 +79,17 @@ exports[`Block Grouping Group creation groups and ungroups multiple blocks of di `; exports[`Block Grouping Preserving selected blocks attributes preserves width alignment settings of selected blocks 1`] = ` -" -
-

Group Heading

+" +
+

Group Heading

- -
\\"\\"/
+ +
- -
\\"\\"/
+ +
diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/block-hierarchy-navigation.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/block-hierarchy-navigation.test.js.snap index 7a9abf86fc42b1..b4d171f170d918 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/block-hierarchy-navigation.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/block-hierarchy-navigation.test.js.snap @@ -6,24 +6,24 @@ exports[`Navigating the block hierarchy should appear and function even without -
\\"\\"/
+
" `; exports[`Navigating the block hierarchy should navigate block hierarchy using only the keyboard 1`] = ` " -
-
+
+

First column

-
+
-
+

Third column

@@ -32,18 +32,18 @@ exports[`Navigating the block hierarchy should navigate block hierarchy using on exports[`Navigating the block hierarchy should navigate using the list view sidebar 1`] = ` " -
-
+
+

First column

-
+
-
+

Third column

@@ -51,13 +51,13 @@ exports[`Navigating the block hierarchy should navigate using the list view side `; exports[`Navigating the block hierarchy should select the wrapper div for a group 1`] = ` -" -
+" +

just a paragraph

-
+
" `; diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/embedding.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/embedding.test.js.snap index 8b3d0323430df9..328b727ac6533b 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/embedding.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/embedding.test.js.snap @@ -2,65 +2,65 @@ exports[`Embedding content should allow the user to convert unembeddable URLs to a paragraph with a link in it 1`] = ` " -

https://twitter.com/wooyaygutenberg123454312

+

https://twitter.com/wooyaygutenberg123454312

" `; exports[`Embedding content should allow the user to try embedding a failed URL again 1`] = ` -" -
+" +
https://twitter.com/wooyaygutenberg123454312
" `; exports[`Embedding content should render embeds in the correct state 1`] = ` -" -
+" +
https://twitter.com/notnownikki
- -
+ +
https://twitter.com/wooyaygutenberg123454312
- -
+ +
https://wordpress.org/gutenberg/handbook/
- -
+ +
https://twitter.com/thatbunty
- -
+ +
https://wordpress.org/gutenberg/handbook/block-api/attributes/
- -
+ +
https://www.youtube.com/watch?v=lXMskKTw3Bc
- -
+ +
https://cloudup.com/cQFlxqtY4ob
" `; exports[`Embedding content should retry embeds that could not be embedded with trailing slashes, without the trailing slashes 1`] = ` -" -
+" +
https://twitter.com/notnownikki/
" diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/inserting-blocks.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/inserting-blocks.test.js.snap index b44c552abc789a..809990c313bc53 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/inserting-blocks.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/inserting-blocks.test.js.snap @@ -54,8 +54,8 @@ lines preserved[/myshortcode] `; exports[`Inserting blocks inserts a block in proper place after having clicked \`Browse All\` from block appender 1`] = ` -" -
+" +

Paragraph inside group

@@ -71,7 +71,7 @@ exports[`Inserting blocks inserts a block in proper place after having clicked \ -

Heading

+

Heading

@@ -88,12 +88,12 @@ exports[`Inserting blocks inserts a block in proper place after having clicked \

First paragraph

- -
+ +
-

Heading

+

Heading

@@ -107,8 +107,8 @@ exports[`Inserting blocks inserts a block in proper place after having clicked \ exports[`Inserting blocks inserts blocks at root level when using the root appender while selection is in an inner block 1`] = ` " -
- +
+
diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/keep-styles-on-block-transforms.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/keep-styles-on-block-transforms.test.js.snap index be070ef9432337..45aaa187a2206a 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/keep-styles-on-block-transforms.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/keep-styles-on-block-transforms.test.js.snap @@ -1,29 +1,29 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Keep styles on block transforms Should keep colors during a transform 1`] = ` -" -

Heading

+" +

Heading

" `; exports[`Keep styles on block transforms Should keep the font size during a transform from multiple blocks into multiple blocks 1`] = ` -" -

Line 1 to be made large

+" +

Line 1 to be made large

- -

Line 2 to be made large

+ +

Line 2 to be made large

- -

Line 3 to be made large

+ +

Line 3 to be made large

" `; exports[`Keep styles on block transforms Should not include styles in the group block when grouping a block 1`] = ` -" -
-

Line 1 to be made large

+" +
+

Line 1 to be made large

" `; diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap index 6ba1f2c0148853..7b54b5bd2f598f 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/links.test.js.snap @@ -8,7 +8,7 @@ exports[`Links allows use of escape key to dismiss the url popover 1`] = ` exports[`Links can be created by selecting text and clicking Link 1`] = ` " -

This is Gutenberg

+

This is Gutenberg

" `; @@ -20,37 +20,37 @@ exports[`Links can be created by selecting text and using keyboard shortcuts 1`] exports[`Links can be created by selecting text and using keyboard shortcuts 2`] = ` " -

This is Gutenberg

+

This is Gutenberg

" `; exports[`Links can be created instantly when a URL is selected 1`] = ` " -

This is Gutenberg: https://wordpress.org/gutenberg

+

This is Gutenberg: https://wordpress.org/gutenberg

" `; exports[`Links can be created without any text selected 1`] = ` " -

This is Gutenberg: https://wordpress.org/gutenberg

+

This is Gutenberg: https://wordpress.org/gutenberg

" `; exports[`Links can be edited 1`] = ` " -

This is Gutenberg

+

This is Gutenberg

" `; exports[`Links can be edited with collapsed selection 1`] = ` " -

This is Gutenberg

+

This is Gutenberg

" `; exports[`Links can be modified using the keyboard once a link has been set 1`] = ` " -

This is Gutenberg.

+

This is Gutenberg.

" `; @@ -62,12 +62,12 @@ exports[`Links can be removed 1`] = ` exports[`Links should contain a label when it should open in a new tab 1`] = ` " -

This is WordPress

+

This is WordPress

" `; exports[`Links should contain a label when it should open in a new tab 2`] = ` " -

This is WordPress

+

This is WordPress

" `; diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap index 7dfe4f68d86818..915b52ba94edab 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap @@ -84,20 +84,20 @@ exports[`Multi-block selection should forward delete across blocks 1`] = ` -

]2

+

]2

" `; exports[`Multi-block selection should forward delete across blocks 2`] = ` " -

1&2

+

1&2

" `; exports[`Multi-block selection should gradually multi-select 1`] = ` " -
-
+
+

1

@@ -107,14 +107,14 @@ exports[`Multi-block selection should gradually multi-select 1`] = ` -
+
" `; exports[`Multi-block selection should gradually multi-select 2`] = ` " -
+
" `; @@ -128,7 +128,7 @@ exports[`Multi-block selection should handle Enter across blocks 1`] = ` -

]2

+

]2

" `; @@ -142,7 +142,7 @@ exports[`Multi-block selection should handle Enter across blocks 2`] = ` -

2

+

2

" `; @@ -251,8 +251,8 @@ exports[`Multi-block selection should preserve dragged selection on move 1`] = ` `; exports[`Multi-block selection should properly select multiple blocks if selected nested blocks belong to different parent 1`] = ` -" -
+" +

first

@@ -261,8 +261,8 @@ exports[`Multi-block selection should properly select multiple blocks if selecte
- -
+ +

second

@@ -300,7 +300,7 @@ exports[`Multi-block selection should select separator (single element block) 1` -
+
" `; @@ -311,12 +311,12 @@ exports[`Multi-block selection should select separator (single element block) 2` `; exports[`Multi-block selection should set attributes for multiple paragraphs 1`] = ` -" -

1

+" +

1

- -

2

+ +

2

" `; diff --git a/packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap b/packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap index 025fa91d03c6fa..5d9c235e1c6a2e 100644 --- a/packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/__snapshots__/rich-text.test.js.snap @@ -31,8 +31,8 @@ exports[`RichText should handle Home and End keys 1`] = ` `; exports[`RichText should handle change in tag name gracefully 1`] = ` -" -

+" +

" `; @@ -136,17 +136,17 @@ exports[`RichText should paste paragraph contents into list 1`] = ` exports[`RichText should preserve internal formatting 1`] = ` " -

1

+

1

" `; exports[`RichText should preserve internal formatting 2`] = ` " -

1

+

1

-

1

+

1

" `; diff --git a/packages/e2e-tests/specs/editor/various/block-grouping.test.js b/packages/e2e-tests/specs/editor/various/block-grouping.test.js index dd51f31016c8d3..07dcabcbf0526d 100644 --- a/packages/e2e-tests/specs/editor/various/block-grouping.test.js +++ b/packages/e2e-tests/specs/editor/various/block-grouping.test.js @@ -148,9 +148,9 @@ describe( 'Block Grouping', () => { await clickBlockToolbarButton( 'Options' ); await clickMenuItem( 'Group' ); expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` - " -
-
+ " +
+

1

diff --git a/packages/e2e-tests/specs/editor/various/format-library/__snapshots__/text-color.test.js.snap b/packages/e2e-tests/specs/editor/various/format-library/__snapshots__/text-color.test.js.snap index 05823957322a11..03bd1c7ddd1d62 100644 --- a/packages/e2e-tests/specs/editor/various/format-library/__snapshots__/text-color.test.js.snap +++ b/packages/e2e-tests/specs/editor/various/format-library/__snapshots__/text-color.test.js.snap @@ -2,7 +2,7 @@ exports[`RichText should remove highlighting element 1`] = ` " -

1

+

1

" `; diff --git a/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js b/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js index 0a4ddaa08ef8a3..9e96c5b14cef49 100644 --- a/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js +++ b/packages/e2e-tests/specs/editor/various/multi-block-selection.test.js @@ -359,8 +359,8 @@ describe( 'Multi-block selection', () => { await page.mouse.up(); await page.keyboard.type( 'hi' ); expect( await getEditedPostContent() ).toMatchInlineSnapshot( ` - " -
+ " +

hih text in group

" diff --git a/packages/e2e-tests/specs/widgets/editing-widgets.test.js b/packages/e2e-tests/specs/widgets/editing-widgets.test.js index bfd7b6b6a6c862..00d34b4ae108d7 100644 --- a/packages/e2e-tests/specs/widgets/editing-widgets.test.js +++ b/packages/e2e-tests/specs/widgets/editing-widgets.test.js @@ -384,6 +384,11 @@ describe( 'Widgets screen', () => { describe( 'Function widgets', () => { async function addMarquee( nbExpectedMarquees ) { + const [ firstWidgetArea ] = await findAll( { + role: 'document', + name: 'Block: Widget Area', + } ); + await firstWidgetArea.focus(); const marqueeBlock = await getBlockInGlobalInserter( 'Marquee Greeting' ); @@ -463,19 +468,19 @@ describe( 'Widgets screen', () => { await saveWidgets(); let editedSerializedWidgetAreas = await getSerializedWidgetAreas(); await expect( editedSerializedWidgetAreas ).toMatchInlineSnapshot( ` - Object { - "sidebar-1": "Howdy", - } - ` ); + { + "sidebar-1": "Howdy", + } + ` ); await page.reload(); editedSerializedWidgetAreas = await getSerializedWidgetAreas(); await expect( editedSerializedWidgetAreas ).toMatchInlineSnapshot( ` - Object { - "sidebar-1": "Howdy", - } - ` ); + { + "sidebar-1": "Howdy", + } + ` ); await addMarquee( 2 ); @@ -493,10 +498,10 @@ describe( 'Widgets screen', () => { await saveWidgets(); editedSerializedWidgetAreas = await getSerializedWidgetAreas(); await expect( editedSerializedWidgetAreas ).toMatchInlineSnapshot( ` - Object { - "sidebar-1": "Howdy", - } - ` ); + { + "sidebar-1": "Howdy", + } + ` ); await page.reload(); const marqueesAfter = await findAll( { @@ -825,11 +830,11 @@ describe( 'Widgets screen', () => { const serializedWidgetAreas = await getSerializedWidgetAreas(); expect( serializedWidgetAreas ).toMatchInlineSnapshot( ` - Object { - "sidebar-1": "
+ { + "sidebar-1": "

First Paragraph

-
+

Second Paragraph

", } diff --git a/packages/edit-site/src/components/navigation-inspector/navigation-menu.js b/packages/edit-site/src/components/navigation-inspector/navigation-menu.js index 731cb3cf9a3b0e..437b95079bd1db 100644 --- a/packages/edit-site/src/components/navigation-inspector/navigation-menu.js +++ b/packages/edit-site/src/components/navigation-inspector/navigation-menu.js @@ -8,7 +8,7 @@ import { BlockTools, } from '@wordpress/block-editor'; import { useEffect } from '@wordpress/element'; -import { useDispatch } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; /** * Internal dependencies @@ -40,6 +40,12 @@ const ALLOWED_BLOCKS = { }; export default function NavigationMenu( { innerBlocks, onSelect } ) { + const { clientIdsTree } = useSelect( ( select ) => { + const { __unstableGetClientIdsTree } = select( blockEditorStore ); + return { + clientIdsTree: __unstableGetClientIdsTree(), + }; + } ); const { updateBlockListSettings } = useDispatch( blockEditorStore ); const { OffCanvasEditor, LeafMoreMenu } = unlock( blockEditorPrivateApis ); @@ -64,7 +70,7 @@ export default function NavigationMenu( { innerBlocks, onSelect } ) { return ( <> diff --git a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergWebViewActivity.java b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergWebViewActivity.java index 2b3302b75e2575..c4ae7e350f4cb7 100644 --- a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergWebViewActivity.java +++ b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergWebViewActivity.java @@ -40,7 +40,7 @@ public class GutenbergWebViewActivity extends AppCompatActivity { public static final String ARG_BLOCK_CONTENT = "block_content"; private static final String INJECT_LOCAL_STORAGE_SCRIPT_TEMPLATE = "localStorage.setItem('WP_DATA_USER_%d','%s')"; - private static final String INJECT_CSS_SCRIPT_TEMPLATE = "window.injectCss('%s')"; + private static final String INJECT_CSS_SCRIPT_TEMPLATE = "window.injectCss('%s', '%s')"; private static final String INJECT_GET_HTML_POST_CONTENT_SCRIPT = "window.getHTMLPostContent();"; private static final String INJECT_ON_SHOW_CONTEXT_MENU_SCRIPT = "window.onShowContextMenu();"; private static final String INJECT_ON_HIDE_CONTEXT_MENU_SCRIPT = "window.onHideContextMenu();"; @@ -327,16 +327,16 @@ private void injectCssScript() { mWebView.evaluateJavascript(injectCssScript, message -> { if (message != null) { String editorStyle = getFileContentFromAssets("gutenberg-web-single-block/editor-style-overrides.css"); - editorStyle = removeWhiteSpace(removeNewLines(editorStyle)); - evaluateJavaScript(String.format(INJECT_CSS_SCRIPT_TEMPLATE, editorStyle)); + editorStyle = removeNewLines(editorStyle); + evaluateJavaScript(String.format(INJECT_CSS_SCRIPT_TEMPLATE, editorStyle, "editor-style-overrides")); String injectWPBarsCssScript = getFileContentFromAssets("gutenberg-web-single-block/wp-bar-override.css"); injectWPBarsCssScript = removeWhiteSpace(removeNewLines(injectWPBarsCssScript)); - evaluateJavaScript(String.format(INJECT_CSS_SCRIPT_TEMPLATE, injectWPBarsCssScript)); + evaluateJavaScript(String.format(INJECT_CSS_SCRIPT_TEMPLATE, injectWPBarsCssScript, "wp-bar-override")); String injectExternalCssScript = getOnGutenbergReadyExternalStyles(); injectExternalCssScript = removeWhiteSpace(removeNewLines(injectExternalCssScript)); - evaluateJavaScript(String.format(INJECT_CSS_SCRIPT_TEMPLATE, injectExternalCssScript)); + evaluateJavaScript(String.format(INJECT_CSS_SCRIPT_TEMPLATE, injectExternalCssScript, "external-styles")); } }); } diff --git a/packages/react-native-bridge/common/gutenberg-web-single-block/content-functions.js b/packages/react-native-bridge/common/gutenberg-web-single-block/content-functions.js index d6e7d51375d441..611701c237b095 100644 --- a/packages/react-native-bridge/common/gutenberg-web-single-block/content-functions.js +++ b/packages/react-native-bridge/common/gutenberg-web-single-block/content-functions.js @@ -25,19 +25,12 @@ window.getHTMLPostContent = () => { }; window.insertBlock = ( blockHTML ) => { - const { blockEditorSelect, blockEditorDispatch } = - window.getBlockEditorStore(); - // Setup the editor with the inserted block. const post = window.wp.data.select( 'core/editor' ).getCurrentPost(); window.wp.data .dispatch( 'core/editor' ) .setupEditor( post, { content: blockHTML } ); - // Select the first block. - const clientId = blockEditorSelect.getBlocks()[ 0 ].clientId; - blockEditorDispatch.selectBlock( clientId ); - window.contentIncerted = true; }; diff --git a/packages/react-native-bridge/common/gutenberg-web-single-block/editor-behavior-overrides.js b/packages/react-native-bridge/common/gutenberg-web-single-block/editor-behavior-overrides.js index 0cfa0e9985fa02..09dcd6447824d7 100644 --- a/packages/react-native-bridge/common/gutenberg-web-single-block/editor-behavior-overrides.js +++ b/packages/react-native-bridge/common/gutenberg-web-single-block/editor-behavior-overrides.js @@ -1,59 +1,167 @@ -// Listeners for native context menu visibility changes. -let isContextMenuVisible = false; -const hideContextMenuListeners = []; - -window.onShowContextMenu = () => { - isContextMenuVisible = true; -}; -window.onHideContextMenu = () => { - isContextMenuVisible = false; - while ( hideContextMenuListeners.length > 0 ) { - const listener = hideContextMenuListeners.pop(); - listener(); - } -}; - -/* -This is a fix for a text selection quirk in the UBE. -It notifies the Android app to dismiss the text selection -context menu when certain menu items are tapped. This is -done via the 'hideTextSelectionContextMenu' method, which -is sent back to the Android app, where the dismissal is -then handle. See PR for further details: -https://github.com/WordPress/gutenberg/pull/34668 -*/ -window.addEventListener( - 'click', - ( event ) => { - const selected = document.getSelection(); - if ( ! isContextMenuVisible || ! selected || ! selected.toString() ) { - return; +/** + * Detects whether the user agent is Android. + * + * @return {boolean} Whether the user agent is Android. + */ +function isAndroid() { + return !! window.navigator.userAgent.match( /Android/ ); +} + +/** + * This is a fix for a text selection quirk in the UBE. It notifies the Android + * app to dismiss the text selection context menu when certain menu items are + * tapped. This is done via the 'hideTextSelectionContextMenu' method, which + * is sent back to the Android app, where the dismissal is then handle. + * + * @return {void} + * @see https://github.com/WordPress/gutenberg/pull/34668 + */ +function manageTextSelectonContextMenu() { + // Listeners for native context menu visibility changes. + let isContextMenuVisible = false; + const hideContextMenuListeners = []; + + window.onShowContextMenu = () => { + isContextMenuVisible = true; + }; + window.onHideContextMenu = () => { + isContextMenuVisible = false; + while ( hideContextMenuListeners.length > 0 ) { + const listener = hideContextMenuListeners.pop(); + listener(); } + }; - // Check if the event is triggered by a dropdown - // toggle button. - const dropdownToggles = document.querySelectorAll( - '.components-dropdown-menu > button' - ); - let currentToggle; - for ( const node of dropdownToggles.values() ) { - if ( node.contains( event.target ) ) { - currentToggle = node; - break; + window.addEventListener( + 'click', + ( event ) => { + const selected = document.getSelection(); + if ( + ! isContextMenuVisible || + ! selected || + ! selected.toString() + ) { + return; } - } - // Hide text selection context menu when the click - // is triggered by a dropdown toggle. - // - // NOTE: The event propagation is prevented because - // it will be dispatched after the context menu - // is hidden. - if ( currentToggle ) { - event.stopPropagation(); - hideContextMenuListeners.push( () => currentToggle.click() ); - window.wpwebkit.hideTextSelectionContextMenu(); + // Check if the event is triggered by a dropdown + // toggle button. + const dropdownToggles = document.querySelectorAll( + '.components-dropdown-menu > button' + ); + let currentToggle; + for ( const node of dropdownToggles.values() ) { + if ( node.contains( event.target ) ) { + currentToggle = node; + break; + } + } + + // Hide text selection context menu when the click + // is triggered by a dropdown toggle. + // + // NOTE: The event propagation is prevented because + // it will be dispatched after the context menu + // is hidden. + if ( currentToggle ) { + event.stopPropagation(); + hideContextMenuListeners.push( () => currentToggle.click() ); + window.wpwebkit.hideTextSelectionContextMenu(); + } + }, + true + ); +} + +if ( isAndroid() ) { + manageTextSelectonContextMenu(); +} + +const editor = document.querySelector( '#editor' ); + +function _toggleBlockSelectedClass( isBlockSelected ) { + if ( isBlockSelected ) { + editor.classList.add( 'is-block-selected' ); + } else { + editor.classList.remove( 'is-block-selected' ); + } +} + +/** @typedef {import('@wordpress/data').WPDataRegistry} WPDataRegistry */ + +/** + * Toggle the `is-block-selected` class on the editor container when a block is + * selected. This is used to hide the sidebar toggle button when a block is not + * selected. + * + * @param {WPDataRegistry} registry Data registry. + * @return {WPDataRegistry} Modified data registry. + */ +function toggleBlockSelectedStyles( registry ) { + return { + dispatch: ( namespace ) => { + const namespaceName = + typeof namespace === 'string' ? namespace : namespace.name; + const actions = { ...registry.dispatch( namespaceName ) }; + + const originalSelectBlockAction = actions.selectBlock; + actions.selectBlock = ( ...args ) => { + _toggleBlockSelectedClass( true ); + return originalSelectBlockAction( ...args ); + }; + + const originalClearSelectedBlockAction = actions.clearSelectedBlock; + actions.clearSelectedBlock = ( ...args ) => { + _toggleBlockSelectedClass( false ); + return originalClearSelectedBlockAction( ...args ); + }; + + return actions; + }, + }; +} + +window.wp.data.use( toggleBlockSelectedStyles ); + +// The editor-canvas iframe relies upon `srcdoc`, which does not trigger a +// `load` event. Thus, we must poll for the iframe to be ready. +let overrideAttempts = 0; +const overrideInterval = setInterval( () => { + overrideAttempts++; + const overrideStyles = document.querySelector( '#editor-style-overrides' ); + const canvasIframe = document.querySelector( + 'iframe[name="editor-canvas"]' + ); + + if ( + overrideStyles && + canvasIframe && + canvasIframe.contentDocument && + canvasIframe.contentDocument.documentElement + ) { + clearInterval( overrideInterval ); + + // Clone the editor styles so that they can be copied to the iframe, as + // elements within an iframe cannot be styled from the parent context. + const overrideStylesClone = overrideStyles.cloneNode( true ); + overrideStylesClone.id = 'editor-styles-overrides-2'; + // Append to document rather than the head, as React will remove this + // mutation. + canvasIframe.contentDocument.documentElement.appendChild( + overrideStylesClone + ); + + // Select the first block. + const { blockEditorSelect, blockEditorDispatch } = + window.getBlockEditorStore(); + const firstBlock = blockEditorSelect.getBlocks()[ 0 ]; + if ( firstBlock ) { + blockEditorDispatch.selectBlock( firstBlock.clientId ); } - }, - true -); + } + + // Safeguard against an infinite loop. + if ( overrideAttempts > 100 ) { + clearInterval( overrideInterval ); + } +}, 300 ); diff --git a/packages/react-native-bridge/common/gutenberg-web-single-block/editor-style-overrides.css b/packages/react-native-bridge/common/gutenberg-web-single-block/editor-style-overrides.css index 7aa208abe55373..f8f2e8fe2b4cde 100644 --- a/packages/react-native-bridge/common/gutenberg-web-single-block/editor-style-overrides.css +++ b/packages/react-native-bridge/common/gutenberg-web-single-block/editor-style-overrides.css @@ -17,14 +17,22 @@ display: none; } -/* - Hiddes the top bar header by setting its height to 0 - We can\'t remove it since the block toolbar is a child of it. - */ +/* Right align post header children as we will only display one child */ .edit-post-header { - height: 0px; - padding: 0px; - overflow: hidden; + justify-content: flex-end; +} + +/* Hide post controls unrelated to editing a single block */ +.edit-post-header__toolbar, +.edit-post-layout .edit-post-header .edit-post-header__settings > *, +.interface-pinned-items > * { + display: none; +} + +/* Display the sidebar toggle button whenever a block is selected */ +.edit-post-layout .edit-post-header .edit-post-header__settings .interface-pinned-items, +.is-block-selected .edit-post-header__settings .interface-pinned-items > button:first-child { + display: flex; } /* Move the block toolbar to the top */ @@ -36,6 +44,11 @@ top: 0px; } +/* Hide block actions unrelated to editing a single block */ +.block-editor-block-settings-menu { + display: none; +} + /* Moves the whole editor to the top. There was an extra top margin after removing the WP Admin bar. @@ -69,38 +82,6 @@ display: none; } -/* - Load second button in component menu group but hide it from view. - This is to fix a Chrome-specific bug that occurs if this button is set to "display: none;" - For additional context, see: https://github.com/WordPress/gutenberg/pull/33740 -*/ -.components-dropdown-menu__menu - > .components-menu-group - > div - > button:nth-child( 2 ) { - display: block; - min-height: 0; - height: 0; - padding: 0; -} - -.components-menu-group > div > button:nth-child( 2 ) > span { - display: none; -} - -.components-button:focus:not( :disabled ) { - box-shadow: none; -} - -/* Remove \'delete block\' button inside \'...\' button in block toolbar */ -.components-dropdown-menu__menu > div:not(:first-child) { - display: none; -} - -.components-dropdown-menu__menu > div:first-child { - padding-bottom: 0; -} - /* Some Themes can overwrite values on \'editor-styles-wrapper\'. This will ensure that the top padding is correct on our single-block version of gutenberg web. diff --git a/packages/react-native-bridge/common/gutenberg-web-single-block/inject-css.js b/packages/react-native-bridge/common/gutenberg-web-single-block/inject-css.js index 60ac677bd20f5c..483e742e780bcb 100644 --- a/packages/react-native-bridge/common/gutenberg-web-single-block/inject-css.js +++ b/packages/react-native-bridge/common/gutenberg-web-single-block/inject-css.js @@ -1,8 +1,9 @@ const injectCss = ` -window.injectCss = (css) => { +window.injectCss = (css, id) => { const style = document.createElement('style'); style.innerHTML = css; style.type = 'text/css'; + style.id = id; document.head.appendChild(style); } `; diff --git a/packages/react-native-bridge/ios/GutenbergWebFallback/FallbackJavascriptInjection.swift b/packages/react-native-bridge/ios/GutenbergWebFallback/FallbackJavascriptInjection.swift index 13ae9dd1f05735..ee75dc0968a586 100644 --- a/packages/react-native-bridge/ios/GutenbergWebFallback/FallbackJavascriptInjection.swift +++ b/packages/react-native-bridge/ios/GutenbergWebFallback/FallbackJavascriptInjection.swift @@ -19,6 +19,7 @@ public struct FallbackJavascriptInjection { public let preventAutosavesScript: WKUserScript public let getHtmlContentScript = "window.getHTMLPostContent()".toJsScript() public let gutenbergObserverScript: WKUserScript + public let editorBehaviorScript: WKUserScript /// Init an instance of GutenbergWebJavascriptInjection or throws if any of the required sources doesn't exist. /// This helps to cach early any possible error due to missing source files. @@ -31,7 +32,7 @@ public struct FallbackJavascriptInjection { } func getInjectCssScript(with source: SourceFile) throws -> WKUserScript { - "window.injectCss(`\(try source.getContent())`)".toJsScript() + "window.injectCss(`\(try source.getContent())`, `\(source.getName())`)".toJsScript() } userContentScripts = [ @@ -44,6 +45,7 @@ public struct FallbackJavascriptInjection { injectEditorCssScript = try getInjectCssScript(with: .editorStyle) preventAutosavesScript = try script(with: .preventAutosaves) gutenbergObserverScript = try script(with: .gutenbergObserver) + editorBehaviorScript = try script(with: .editorBehavior) let localStorageJsonString = try SourceFile.localStorage.getContent().removingSpacesAndNewLines() let scriptString = String(format: injectLocalStorageScriptTemplate, userId, localStorageJsonString) diff --git a/packages/react-native-bridge/ios/GutenbergWebFallback/GutenbergWebSingleBlockViewController.swift b/packages/react-native-bridge/ios/GutenbergWebFallback/GutenbergWebSingleBlockViewController.swift index aa83095f058f10..8d11028728a891 100644 --- a/packages/react-native-bridge/ios/GutenbergWebFallback/GutenbergWebSingleBlockViewController.swift +++ b/packages/react-native-bridge/ios/GutenbergWebFallback/GutenbergWebSingleBlockViewController.swift @@ -76,6 +76,7 @@ open class GutenbergWebSingleBlockViewController: UIViewController { onGutenbergReadyScripts().forEach(evaluateJavascript) evaluateJavascript(jsInjection.preventAutosavesScript) evaluateJavascript(jsInjection.insertBlockScript) + evaluateJavascript(jsInjection.editorBehaviorScript) DispatchQueue.main.async { [weak self] in self?.removeCoverViewAnimated() } diff --git a/packages/react-native-bridge/ios/SourceFile.swift b/packages/react-native-bridge/ios/SourceFile.swift index 2f75e22278c0f1..2386f84580464f 100644 --- a/packages/react-native-bridge/ios/SourceFile.swift +++ b/packages/react-native-bridge/ios/SourceFile.swift @@ -29,6 +29,10 @@ public struct SourceFile { } extension SourceFile { + public func getName() -> String { + return self.name + } + public func jsScript(with argument: String? = nil) throws -> WKUserScript { let content = try getContent() let formatted = String(format: content, argument ?? []) @@ -53,4 +57,5 @@ extension SourceFile { static let preventAutosaves = SourceFile(name: "prevent-autosaves", type: .js) static let gutenbergObserver = SourceFile(name: "gutenberg-observer", type: .js) static let supportedBlocks = SourceFile(name: "supported-blocks", type: .json) + static let editorBehavior = SourceFile(name: "editor-behavior-overrides", type: .js) } diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 8b8c395a67d9e9..e34f5bfd753ee2 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -12,6 +12,9 @@ For each user feature we should also add a importance categorization label to i ## Unreleased - [*] Add metadata parameter to media upload events [#48103] +## 1.89.1 +- [*] Fix inaccessible block settings within the unsupported block editor [#48435] + ## 1.89.0 * No User facing changes * diff --git a/packages/react-native-editor/src/parser/block-parser-code.test.js b/packages/react-native-editor/src/test/block-parser-code.test.js similarity index 94% rename from packages/react-native-editor/src/parser/block-parser-code.test.js rename to packages/react-native-editor/src/test/block-parser-code.test.js index 276e73b6d7d5b7..e53433f67c5ec5 100644 --- a/packages/react-native-editor/src/parser/block-parser-code.test.js +++ b/packages/react-native-editor/src/test/block-parser-code.test.js @@ -1,8 +1,3 @@ -/** - * Internal dependencies - */ -import '../globals'; - /** * WordPress dependencies */ diff --git a/packages/react-native-editor/src/parser/block-parser-more.test.js b/packages/react-native-editor/src/test/block-parser-more.test.js similarity index 94% rename from packages/react-native-editor/src/parser/block-parser-more.test.js rename to packages/react-native-editor/src/test/block-parser-more.test.js index c2f2360fc64a2c..4b7b91d5a90c72 100644 --- a/packages/react-native-editor/src/parser/block-parser-more.test.js +++ b/packages/react-native-editor/src/test/block-parser-more.test.js @@ -1,8 +1,3 @@ -/** - * Internal dependencies - */ -import '../globals'; - /** * WordPress dependencies */ diff --git a/packages/react-native-editor/src/parser/block-parser-paragraph.test.js b/packages/react-native-editor/src/test/block-parser-paragraph.test.js similarity index 94% rename from packages/react-native-editor/src/parser/block-parser-paragraph.test.js rename to packages/react-native-editor/src/test/block-parser-paragraph.test.js index d9f451005901a4..823b85e4e29ccc 100644 --- a/packages/react-native-editor/src/parser/block-parser-paragraph.test.js +++ b/packages/react-native-editor/src/test/block-parser-paragraph.test.js @@ -1,8 +1,3 @@ -/** - * Internal dependencies - */ -import '../globals'; - /** * WordPress dependencies */ diff --git a/packages/scripts/scripts/test-e2e.js b/packages/scripts/scripts/test-e2e.js index 67927038c7eeb8..330439ce2121b7 100644 --- a/packages/scripts/scripts/test-e2e.js +++ b/packages/scripts/scripts/test-e2e.js @@ -51,6 +51,7 @@ const config = configFile ? [ '--config', JSON.stringify( require( configFile ) ) ] : []; +// Force e2e tests to run serially, not in parallel. They test against a shared Docker instance const hasRunInBand = hasArgInCLI( '--runInBand' ) || hasArgInCLI( '-i' ); const runInBand = ! hasRunInBand ? [ '--runInBand' ] : []; diff --git a/test/e2e/specs/editor/various/block-deletion.spec.js b/test/e2e/specs/editor/various/block-deletion.spec.js index efcf7b7d5a2825..78c63bfc64aac0 100644 --- a/test/e2e/specs/editor/various/block-deletion.spec.js +++ b/test/e2e/specs/editor/various/block-deletion.spec.js @@ -62,6 +62,53 @@ test.describe( 'Block deletion', () => { ] ); } ); + // this test should be moved to a new testing story about focus management. + test( 'deleting a block focuses the parent block', async ( { + editor, + page, + } ) => { + // Add a group with a paragraph in it. + await editor.insertBlock( { + name: 'core/group', + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { content: 'Paragraph child of group' }, + }, + ], + } ); + + // Select the paragraph. + const paragraph = editor.canvas.getByRole( 'document', { + name: 'Paragraph block', + } ); + await editor.selectBlocks( paragraph ); + + // Remove the current paragraph via the Block Toolbar options menu. + await editor.showBlockToolbar(); + await editor.canvas + .getByRole( 'toolbar', { name: 'Block tools' } ) + .getByRole( 'button', { name: 'Options' } ) + .click(); + await page + .getByRole( 'menuitem', { name: 'Remove Paragraph' } ) + .click(); + + // Ensure the paragraph was removed. + await expect + .poll( editor.getBlocks ) + .toMatchObject( [ { name: 'core/group', attributes: {} } ] ); + + // Ensure the group block is focused. + await expect( + editor.canvas + .getByRole( 'document', { + name: 'Block: Group', + } ) + .last() + ).toBeFocused(); + } ); + test( 'deleting the last block via the keyboard shortcut', async ( { editor, page, diff --git a/test/native/jest.config.js b/test/native/jest.config.js index 8492ca88746a40..14a901281f394c 100644 --- a/test/native/jest.config.js +++ b/test/native/jest.config.js @@ -31,12 +31,7 @@ module.exports = { '**/test/!(helper)*.native.[jt]s?(x)', '/packages/react-native-*/**/?(*.)+(spec|test).[jt]s?(x)', ], - testPathIgnorePatterns: [ - '/node_modules/', - '/__device-tests__/', - '/.*/build/', - '/.*/build-module/', - ], + testPathIgnorePatterns: [ '/node_modules/', '/__device-tests__/' ], testURL: 'http://localhost/', // Add the `Libraries/Utilities` subfolder to the module directories, otherwise haste/jest doesn't find Platform.js on Travis, // and add it first so https://github.com/facebook/react-native/blob/v0.60.0/Libraries/react-native/react-native-implementation.js#L324-L326 doesn't pick up the Platform npm module.