diff --git a/docs/reference-guides/block-api/block-metadata.md b/docs/reference-guides/block-api/block-metadata.md index d048cc78fede6..63219182dd1c2 100644 --- a/docs/reference-guides/block-api/block-metadata.md +++ b/docs/reference-guides/block-api/block-metadata.md @@ -28,9 +28,6 @@ Starting in WordPress 5.8 release, we encourage using the `block.json` metadata "my-plugin/message": "message" }, "usesContext": [ "groupId" ], - "editorSelectors": { - "root": ".editor-styles-wrapper .wp-block-my-plugin-notice" - }, "selectors": { "root": ".wp-block-my-plugin-notice" }, @@ -385,38 +382,6 @@ See [the block context documentation](/docs/reference-guides/block-api/block-con } ``` -### Editor Selectors - -- Type: `object` -- Optional -- Localized: No -- Property: `editorSelectors` -- Default: `{}` -- Since: `WordPress 6.3.0` - -Any editor specific custom CSS selectors, keyed by `root`, feature, or -sub-feature, to be used when generating block styles for theme.json -(global styles) stylesheets in the editor. - -Editor only selectors override those defined within the `selectors` property. - -See the [the selectors documentation](/docs/reference-guides/block-api/block-selectors.md) for more details. - -```json -{ - "editorSelectors": { - "root": ".my-custom-block-selector", - "color": { - "text": ".my-custom-block-selector p" - }, - "typography": { - "root": ".my-custom-block-selector > h2", - "text-decoration": ".my-custom-block-selector > h2 span" - } - } -} -``` - ### Selectors - Type: `object` diff --git a/docs/reference-guides/block-api/block-selectors.md b/docs/reference-guides/block-api/block-selectors.md index 6560827bc54e2..9ce605782c628 100644 --- a/docs/reference-guides/block-api/block-selectors.md +++ b/docs/reference-guides/block-api/block-selectors.md @@ -4,7 +4,7 @@ Block Selectors is the API that allows blocks to customize the CSS selector used when their styles are generated. A block may customize its CSS selectors at three levels: root, feature, and -subfeature. Each may also be overridden with editor-only selectors. +subfeature. ## Root Selector @@ -113,33 +113,4 @@ example. As the `color` feature also doesn't define a `root` selector, selector, `.my-custom-block-selector`. For a subfeature such as `typography.font-size`, it would fallback to its parent -feature's selector given that is present, i.e. `.my-custom-block-selector > h2`. - -## Editor-only Selectors - -There are scenarios in which a block might need different markup within the -editor compared to the frontend e.g. inline cropping of the Image block. Some -generated styles may then need to be applied to different, or multiple, -elements. - -Continuing with the Image cropping example, the image border styles need to also -be applied to the cropping area. If the selector for the cropping area is added -to the normal `selectors` config for the block, it would be output unnecessarily -on the frontend. - -To avoid this, and include the selector for the editor only, the selectors for the border feature can be -overridden via the `editorSelectors` config. - -### Example -```json -{ - ... - "selectors": { - "root": ".wp-block-image", - "border": ".wp-block-image img" - }, - "editorSelectors": { - "border": ".wp-block-image img, .wp-block-image .wp-block-image__crop-area" - }, -} -``` +feature's selector given that is present, i.e. `.my-custom-block-selector > h2`. \ No newline at end of file diff --git a/lib/class-wp-duotone-gutenberg.php b/lib/class-wp-duotone-gutenberg.php index 090696fca0a14..7e1ad79c93bff 100644 --- a/lib/class-wp-duotone-gutenberg.php +++ b/lib/class-wp-duotone-gutenberg.php @@ -235,9 +235,11 @@ public static function output_global_styles() { public static function render_duotone_support( $block_content, $block ) { $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] ); - $duotone_support = false; - if ( $block_type && property_exists( $block_type, 'supports' ) ) { - $duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false ); + $duotone_support = false; + $duotone_selector = null; + if ( $block_type ) { + $duotone_selector = wp_get_block_css_selector( $block_type, 'filter.duotone' ); + $duotone_support = (bool) $duotone_selector; } // The block should have a duotone attribute or have duotone defined in its theme.json to be processed. @@ -308,7 +310,7 @@ public static function render_duotone_support( $block_content, $block ) { $filter_id = gutenberg_get_duotone_filter_id( array( 'slug' => $slug ) ); // Build the CSS selectors to which the filter will be applied. - $selector = WP_Theme_JSON_Gutenberg::scope_selector( '.' . $filter_id, $duotone_support ); + $selector = WP_Theme_JSON_Gutenberg::scope_selector( '.' . $filter_id, $duotone_selector ); // We only want to add the selector if we have it in the output already, essentially skipping 'unset'. if ( array_key_exists( $slug, self::$output ) ) { diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index b0367bb5c779d..c7e1307f4a0bf 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -870,7 +870,7 @@ protected static function get_blocks_metadata() { } // The block may or may not have a duotone selector. - $duotone_selector = wp_get_block_css_selector( $block_type, 'filters.duotone' ); + $duotone_selector = wp_get_block_css_selector( $block_type, 'filter.duotone' ); if ( null !== $duotone_selector ) { static::$blocks_metadata[ $block_name ]['duotone'] = $duotone_selector; } @@ -2269,57 +2269,39 @@ public function get_styles_for_block( $block_metadata ) { $selector = $block_metadata['selector']; $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); - $feature_declarations = static::get_feature_declarations_for_block( $block_metadata, $node, $settings, $this->theme_json ); + $feature_declarations = static::get_feature_declarations_for_node( $block_metadata, $node ); // If there are style variations, generate the declarations for them, including any feature selectors the block may have. $style_variation_declarations = array(); if ( ! empty( $block_metadata['variations'] ) ) { foreach ( $block_metadata['variations'] as $style_variation ) { - $style_variation_node = _wp_array_get( $this->theme_json, $style_variation['path'], array() ); - $style_variation_selector = $style_variation['selector']; - - // If the block has feature selectors, generate the declarations for them within the current style variation. - if ( ! empty( $block_metadata['features'] ) ) { - $clean_style_variation_selector = trim( $style_variation_selector ); - foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) { - if ( empty( $style_variation_node[ $feature_name ] ) ) { - continue; - } - // If feature selector includes block classname, remove it but leave the whitespace in. - $shortened_feature_selector = str_replace( $block_metadata['selector'] . ' ', ' ', $feature_selector ); - // Prepend the variation selector to the feature selector. - $split_feature_selectors = explode( ',', $shortened_feature_selector ); - $feature_selectors = array_map( - static function( $split_feature_selector ) use ( $clean_style_variation_selector ) { - return $clean_style_variation_selector . $split_feature_selector; - }, - $split_feature_selectors - ); - $combined_feature_selectors = implode( ',', $feature_selectors ); - - // Compute declarations for the feature. - $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. - */ - 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 { - $style_variation_declarations[ $combined_feature_selectors ] = $new_feature_declarations; - } + $style_variation_node = _wp_array_get( $this->theme_json, $style_variation['path'], array() ); + $clean_style_variation_selector = trim( $style_variation['selector'] ); + + // Generate any feature/subfeature style declarations for the current style variation. + $variation_declarations = static::get_feature_declarations_for_node( $block_metadata, $style_variation_node ); + + // Combine selectors with style variation's selector and add to overall style variation declarations. + foreach ( $variation_declarations as $current_selector => $new_declarations ) { + // If current selector includes block classname, remove it but leave the whitespace in. + $shortened_selector = str_replace( $block_metadata['selector'] . ' ', ' ', $current_selector ); + + // Prepend the variation selector to the current selector. + $split_selectors = explode( ',', $shortened_selector ); + $updated_selectors = array_map( + static function( $split_selector ) use ( $clean_style_variation_selector ) { + return $clean_style_variation_selector . $split_selector; + }, + $split_selectors + ); + $combined_selectors = implode( ',', $updated_selectors ); - /* - * 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 ] ); - } + // Add the new declarations to the overall results under the modified selector. + $style_variation_declarations[ $combined_selectors ] = $new_declarations; } + // Compute declarations for remaining styles not covered by feature level selectors. - $style_variation_declarations[ $style_variation_selector ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json ); + $style_variation_declarations[ $style_variation['selector'] ] = static::compute_style_properties( $style_variation_node, $settings, null, $this->theme_json ); } } @@ -3420,32 +3402,6 @@ public function set_spacing_sizes() { _wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes ); } - /** - * Generates the root selector for a block. - * - * @param object $block_type The block type. - * @return string - */ - protected static function get_root_block_selector( $block_type ) { - // Prefer the selectors API if available. - if ( isset( $block_type->selectors ) && - isset( $block_type->selectors['root'] ) - ) { - return $block_type->selectors['root']; - } - - // Use the old experimental selector supports property if set. - if ( isset( $block_type->supports['__experimentalSelector'] ) && - is_string( $block_type->supports['__experimentalSelector'] ) ) { - return $block_type->supports['__experimentalSelector']; - } - - // Generate default block class selector. - $block_name = str_replace( '/', '-', str_replace( 'core/', '', $block_type->name ) ); - - return ".wp-block-{$block_name}"; - } - /** * Returns the selectors metadata for a block. * @@ -3456,17 +3412,6 @@ protected static function get_root_block_selector( $block_type ) { */ protected static function get_block_selectors( $block_type, $root_selector ) { if ( ! empty( $block_type->selectors ) ) { - $in_editor = false; - - if ( function_exists( 'get_current_screen' ) ) { - $current_screen = get_current_screen(); - $in_editor = $current_screen && $current_screen->is_block_editor; - } - - if ( $in_editor && ! empty( $block_type->editor_selectors ) ) { - return array_merge( $block_type->selectors, $block_type->editor_selectors ); - } - return $block_type->selectors; } @@ -3509,37 +3454,36 @@ protected static function get_block_element_selectors( $root_selector ) { return $element_selectors; } - /** - * Generates style declarations for the block's features e.g. color, border, - * typography etc, that have custom selectors in their block metadata. + * Generates style declarations for a node's features e.g. color, border, + * typography etc, that have custom selectors in their related block's + * metadata. * - * @param object $block_metadata The block's metadata containing selectors for - * features. - * @param object $block_node The merged theme.json node for the block. - * @param object $settings The theme.json settings for the node. - * @param object $theme_json The current theme.json config. + * @param object $metadata The related block metadata containing selectors. + * @param object $node A merged theme.json node for block or variation. * - * @return array The style declarations for the block's features with custom + * @return array The style declarations for the node's features with custom * selectors. */ - protected static function get_feature_declarations_for_block( $block_metadata, &$block_node, $settings, $theme_json ) { + protected function get_feature_declarations_for_node( $metadata, &$node ) { $declarations = array(); - if ( ! isset( $block_metadata['selectors'] ) ) { + if ( ! isset( $metadata['selectors'] ) ) { return $declarations; } - foreach ( $block_metadata['selectors'] as $feature => $feature_selectors ) { + $settings = _wp_array_get( $this->theme_json, array( 'settings' ) ); + + foreach ( $metadata['selectors'] as $feature => $feature_selectors ) { // Skip if this is the block's root selector or the block doesn't // have any styles for the feature. - if ( 'root' === $feature || empty( $block_node[ $feature ] ) ) { + if ( 'root' === $feature || empty( $node[ $feature ] ) ) { continue; } if ( is_array( $feature_selectors ) ) { foreach ( $feature_selectors as $subfeature => $subfeature_selector ) { - if ( 'root' === $subfeature || empty( $block_node[ $feature ][ $subfeature ] ) ) { + if ( 'root' === $subfeature || empty( $node[ $feature ][ $subfeature ] ) ) { continue; } @@ -3547,12 +3491,12 @@ protected static function get_feature_declarations_for_block( $block_metadata, & // to leverage existing `compute_style_properties` function. $subfeature_node = array( $feature => array( - $subfeature => $block_node[ $feature ][ $subfeature ], + $subfeature => $node[ $feature ][ $subfeature ], ), ); // Generate style declarations. - $new_declarations = static::compute_style_properties( $subfeature_node, $settings, null, $theme_json ); + $new_declarations = static::compute_style_properties( $subfeature_node, $settings, null, $this->theme_json ); // Merge subfeature declarations into feature declarations. if ( isset( $declarations[ $subfeature_selector ] ) ) { @@ -3566,7 +3510,7 @@ protected static function get_feature_declarations_for_block( $block_metadata, & // Remove the subfeature from the block's node now its // styles will be included under its own selector not the // block's. - unset( $block_node[ $feature ][ $subfeature ] ); + unset( $node[ $feature ][ $subfeature ] ); } } @@ -3580,10 +3524,10 @@ protected static function get_feature_declarations_for_block( $block_metadata, & // Create temporary node containing only the feature data // to leverage existing `compute_style_properties` function. - $feature_node = array( $feature => $block_node[ $feature ] ); + $feature_node = array( $feature => $node[ $feature ] ); // Generate the style declarations. - $new_declarations = static::compute_style_properties( $feature_node, $settings, null, $theme_json ); + $new_declarations = static::compute_style_properties( $feature_node, $settings, null, $this->theme_json ); // Merge new declarations with any that already exist for // the feature selector. This may occur when multiple block @@ -3598,7 +3542,7 @@ protected static function get_feature_declarations_for_block( $block_metadata, & // Remove the feature from the block's node now its styles // will be included under its own selector not the block's. - unset( $block_node[ $feature ] ); + unset( $node[ $feature ] ); } } diff --git a/lib/compat/wordpress-6.3/blocks.php b/lib/compat/wordpress-6.3/blocks.php index 8866edf5b95fd..5f017997f52b5 100644 --- a/lib/compat/wordpress-6.3/blocks.php +++ b/lib/compat/wordpress-6.3/blocks.php @@ -6,10 +6,10 @@ */ /** - * Ensure the selectors, and editorSelectors properties, set via block.json - * metadata, are included within the block type's settings. + * Ensure the selectors, set via block.json metadata, are included within the + * block type's settings. * - * Note: This should be removed when the minimum required WP version is >= 6.2. + * Note: This should be removed when the minimum required WP version is >= 6.3. * * @see https://github.com/WordPress/gutenberg/pull/46496 * @@ -18,15 +18,11 @@ * * @return array Filtered block type settings. */ -function gutenberg_add_selectors_properties_to_block_type_settings( $settings, $metadata ) { +function gutenberg_add_selectors_property_to_block_type_settings( $settings, $metadata ) { if ( ! isset( $settings['selectors'] ) && isset( $metadata['selectors'] ) ) { $settings['selectors'] = $metadata['selectors']; } - if ( ! isset( $settings['editor_selectors'] ) && isset( $metadata['editorSelectors'] ) ) { - $settings['editor_selectors'] = $metadata['editorSelectors']; - } - return $settings; } -add_filter( 'block_type_metadata_settings', 'gutenberg_add_selectors_properties_to_block_type_settings', 10, 2 ); +add_filter( 'block_type_metadata_settings', 'gutenberg_add_selectors_property_to_block_type_settings', 10, 2 ); diff --git a/lib/compat/wordpress-6.3/get-global-styles-and-settings.php b/lib/compat/wordpress-6.3/get-global-styles-and-settings.php index 5b4ff3a5dd5cb..c213e50d64ae7 100644 --- a/lib/compat/wordpress-6.3/get-global-styles-and-settings.php +++ b/lib/compat/wordpress-6.3/get-global-styles-and-settings.php @@ -23,30 +23,13 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f return null; } - $has_selectors = ! empty( $block_type->selectors ); - $use_editor_selectors = false; - - // Determine if we are in the editor and require editor selectors - // if they are available. - if ( function_exists( 'get_current_screen' ) ) { - $current_screen = get_current_screen(); - $use_editor_selectors = ! empty( $block_type->editor_selectors ) && $current_screen && $current_screen->is_block_editor; - } + $has_selectors = ! empty( $block_type->selectors ); // Duotone (No fallback selectors for Duotone). - if ( 'filters.duotone' === $target || array( 'filters', 'duotone' ) === $target ) { - // Prefer editor selector if available. - $duotone_editor_selector = $use_editor_selectors - ? _wp_array_get( $block_type->editor_selectors, array( 'filters', 'duotone' ), null ) - : null; - - if ( $duotone_editor_selector ) { - return $duotone_editor_selector; - } - + if ( 'filter.duotone' === $target || array( 'filter', 'duotone' ) === $target ) { // If selectors API in use, only use it's value or null. if ( $has_selectors ) { - return _wp_array_get( $block_type->selectors, array( 'filters', 'duotone' ), null ); + return _wp_array_get( $block_type->selectors, array( 'filter', 'duotone' ), null ); } // Selectors API, not available, check for old experimental selector. @@ -59,10 +42,7 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f // feature selectors later on. $root_selector = null; - if ( $use_editor_selectors && isset( $block_type->editor_selectors['root'] ) ) { - // Prefer editor selectors if specified. - $root_selector = $block_type->editor_selectors['root']; - } elseif ( $has_selectors && isset( $block_type->selectors['root'] ) ) { + if ( $has_selectors && isset( $block_type->selectors['root'] ) ) { // Use the selectors API if available. $root_selector = $block_type->selectors['root']; } elseif ( isset( $block_type->supports['__experimentalSelector'] ) && is_string( $block_type->supports['__experimentalSelector'] ) ) { @@ -89,35 +69,17 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f if ( 1 === count( $target ) ) { $fallback_selector = $fallback ? $root_selector : null; - // Look for selector under `feature.root`. - $path = array_merge( $target, array( 'root' ) ); - - // Use editor specific selector if available. - if ( $use_editor_selectors ) { - $feature_selector = _wp_array_get( $block_type->editor_selectors, $path, null ); - - if ( $feature_selector ) { - return $feature_selector; - } - - // Check if feature selector set via shorthand. - $feature_selector = _wp_array_get( $block_type->editor_selectors, $target, null ); - - // Only return if a selector was found. - if ( is_string( $feature_selector ) ) { - return $feature_selector; - } - } - // Prefer the selectors API if available. if ( $has_selectors ) { + // Look for selector under `feature.root`. + $path = array_merge( $target, array( 'root' ) ); $feature_selector = _wp_array_get( $block_type->selectors, $path, null ); if ( $feature_selector ) { return $feature_selector; } - // Check if feature selector set via shorthand. + // Check if feature selector is set via shorthand. $feature_selector = _wp_array_get( $block_type->selectors, $target, null ); return is_string( $feature_selector ) ? $feature_selector : $fallback_selector; @@ -158,15 +120,8 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f // This may fallback either to parent feature or root selector. $subfeature_selector = null; - // Use any explicit editor selector. Subfeature editor-only selectors - // will not fall back to the feature's editor specific selector if - // the normal selectors object contains a selector for the subfeature. - if ( $use_editor_selectors ) { - $subfeature_selector = _wp_array_get( $block_type->editor_selectors, $target, null ); - } - // Use selectors API if available. - if ( $has_selectors && ! $subfeature_selector ) { + if ( $has_selectors ) { $subfeature_selector = _wp_array_get( $block_type->selectors, $target, null ); } diff --git a/packages/block-editor/src/components/global-styles/get-block-css-selector.js b/packages/block-editor/src/components/global-styles/get-block-css-selector.js new file mode 100644 index 0000000000000..db58709fe79aa --- /dev/null +++ b/packages/block-editor/src/components/global-styles/get-block-css-selector.js @@ -0,0 +1,129 @@ +/** + * External dependencies + */ +import { get, isEmpty } from 'lodash'; + +/** + * Internal dependencies + */ +import { scopeSelector } from './utils'; + +/** + * Determine the CSS selector for the block type and target provided, returning + * it if available. + * + * @param {import('@wordpress/blocks').Block} blockType The block's type. + * @param {string|string[]} target The desired selector's target e.g. `root`, delimited string, or array path. + * @param {Object} options Options object. + * @param {boolean} options.fallback Whether or not to fallback to broader selector. + * + * @return {?string} The CSS selector or `null` if no selector available. + */ +export function getBlockCSSSelector( + blockType, + target = 'root', + options = {} +) { + if ( ! target ) { + return null; + } + + const { fallback = false } = options; + const { name, selectors, supports } = blockType; + + const hasSelectors = ! isEmpty( selectors ); + const path = Array.isArray( target ) ? target.join( '.' ) : target; + + // Duotone ( no fallback selectors for Duotone ). + if ( path === 'filter.duotone' ) { + // If selectors API in use, only use its value or null. + if ( hasSelectors ) { + return get( selectors, path, null ); + } + + // Selectors API, not available, check for old experimental selector. + return get( supports, 'color.__experimentalDuotone', null ); + } + + // Root selector. + + // Calculated before returning as it can be used as a fallback for feature + // selectors later on. + let rootSelector = null; + + if ( hasSelectors && selectors.root ) { + // Use the selectors API if available. + rootSelector = selectors?.root; + } else if ( supports?.__experimentalSelector ) { + // Use the old experimental selector supports property if set. + rootSelector = supports.__experimentalSelector; + } else { + // If no root selector found, generate default block class selector. + rootSelector = + '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' ); + } + + // Return selector if it's the root target we are looking for. + if ( path === 'root' ) { + return rootSelector; + } + + // If target is not `root` or `duotone` we have a feature or subfeature + // as the target. If the target is a string convert to an array. + const pathArray = Array.isArray( target ) ? target : target.split( '.' ); + + // Feature selectors ( may fallback to root selector ); + if ( pathArray.length === 1 ) { + const fallbackSelector = fallback ? rootSelector : null; + + // Prefer the selectors API if available. + if ( hasSelectors ) { + // Get selector from either `feature.root` or shorthand path. + const featureSelector = + get( selectors, `${ path }.root`, null ) || + get( selectors, path, null ); + + // Return feature selector if found or any available fallback. + return featureSelector || fallbackSelector; + } + + // Try getting old experimental supports selector value. + const featureSelector = get( + supports, + `${ path }.__experimentalSelector`, + null + ); + + // If nothing to work with, provide fallback selector if available. + if ( ! featureSelector ) { + return fallbackSelector; + } + + // Scope the feature selector by the block's root selector. + return scopeSelector( rootSelector, featureSelector ); + } + + // Subfeature selector. + // This may fallback either to parent feature or root selector. + let subfeatureSelector; + + // Use selectors API if available. + if ( hasSelectors ) { + subfeatureSelector = get( selectors, path, null ); + } + + // Only return if we have a subfeature selector. + if ( subfeatureSelector ) { + return subfeatureSelector; + } + + // To this point we don't have a subfeature selector. If a fallback has been + // requested, remove subfeature from target path and return results of a + // call for the parent feature's selector. + if ( fallback ) { + return getBlockCSSSelector( blockType, pathArray[ 0 ], options ); + } + + // We tried. + return null; +} diff --git a/packages/block-editor/src/components/global-styles/index.js b/packages/block-editor/src/components/global-styles/index.js index 5c344cea3924e..97238de17a212 100644 --- a/packages/block-editor/src/components/global-styles/index.js +++ b/packages/block-editor/src/components/global-styles/index.js @@ -4,6 +4,7 @@ export { useGlobalStyle, useSettingsForBlockElement, } from './hooks'; +export { getBlockCSSSelector } from './get-block-css-selector'; export { useGlobalStylesOutput } from './use-global-styles-output'; export { GlobalStylesContext } from './context'; export { diff --git a/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js index 1848906ed1fcd..a826c56204478 100644 --- a/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js @@ -480,7 +480,7 @@ describe( 'global styles renderer', () => { expect( toStyles( tree, blockSelectors ) ).toEqual( 'body {margin: 0;}' + 'body{background-color: red;margin: 10px;padding: 10px;}a{color: blue;}a:hover{color: orange;}a:focus{color: orange;}h1{font-size: 42px;}.wp-block-group{margin-top: 10px;margin-right: 20px;margin-bottom: 30px;margin-left: 40px;padding-top: 11px;padding-right: 22px;padding-bottom: 33px;padding-left: 44px;}h1,h2,h3,h4,h5,h6{color: orange;}h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color: hotpink;}h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{color: red;}h1 a:focus,h2 a:focus,h3 a:focus,h4 a:focus,h5 a:focus,h6 a:focus{color: red;}' + - '.wp-block-image img, .wp-block-image .wp-crop-area{border-radius: 9999px }.wp-block-image{color: red;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }' + + '.wp-block-image img, .wp-block-image .wp-crop-area{border-radius: 9999px}.wp-block-image{color: red;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }' + '.has-white-color{color: var(--wp--preset--color--white) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}h1.has-blue-color,h2.has-blue-color,h3.has-blue-color,h4.has-blue-color,h5.has-blue-color,h6.has-blue-color{color: var(--wp--preset--color--blue) !important;}h1.has-blue-background-color,h2.has-blue-background-color,h3.has-blue-background-color,h4.has-blue-background-color,h5.has-blue-background-color,h6.has-blue-background-color{background-color: var(--wp--preset--color--blue) !important;}h1.has-blue-border-color,h2.has-blue-border-color,h3.has-blue-border-color,h4.has-blue-border-color,h5.has-blue-border-color,h6.has-blue-border-color{border-color: var(--wp--preset--color--blue) !important;}' ); } ); @@ -668,6 +668,34 @@ describe( 'global styles renderer', () => { describe( 'getBlockSelectors', () => { it( 'should return block selectors data', () => { + const imageSelectors = { + root: '.my-image', + border: '.my-image img, .my-image .crop-area', + filter: { duotone: 'img' }, + }; + const imageBlock = { + name: 'core/image', + selectors: imageSelectors, + }; + const blockTypes = [ imageBlock ]; + + expect( getBlockSelectors( blockTypes, () => {} ) ).toEqual( { + 'core/image': { + name: imageBlock.name, + selector: imageSelectors.root, + duotoneSelector: imageSelectors.filter.duotone, + fallbackGapValue: undefined, + featureSelectors: { + root: '.my-image', + border: '.my-image img, .my-image .crop-area', + filter: { duotone: 'img' }, + }, + hasLayoutSupport: false, + }, + } ); + } ); + + it( 'should return block selectors data with old experimental selectors', () => { const imageSupports = { __experimentalBorder: { radius: true, @@ -688,6 +716,7 @@ describe( 'global styles renderer', () => { duotoneSelector: imageSupports.color.__experimentalDuotone, fallbackGapValue: undefined, featureSelectors: { + root: '.my-image', border: '.my-image img, .my-image .crop-area', }, hasLayoutSupport: false, diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js index cce2b61f0786e..20fa92e843f31 100644 --- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js @@ -20,6 +20,7 @@ import { getCSSRules } from '@wordpress/style-engine'; * Internal dependencies */ import { PRESET_METADATA, ROOT_BLOCK_SELECTOR, scopeSelector } from './utils'; +import { getBlockCSSSelector } from './get-block-css-selector'; import { getTypographyFontSizeValue } from './typography-utils'; import { GlobalStylesContext } from './context'; import { useGlobalSetting } from './hooks'; @@ -193,6 +194,89 @@ function concatFeatureVariationSelectorString( return combinedSelectors.join( ', ' ); } +/** + * Generate style declarations for a block's custom feature and subfeature + * selectors. + * + * NOTE: The passed `styles` object will be mutated by this function. + * + * @param {Object} selectors Custom selectors object for a block. + * @param {Object} styles A block's styles object. + * + * @return {Object} Style declarations. + */ +const getFeatureDeclarations = ( selectors, styles ) => { + const declarations = {}; + + Object.entries( selectors ).forEach( ( [ feature, selector ] ) => { + // We're only processing features/subfeatures that have styles. + if ( feature === 'root' || ! styles?.[ feature ] ) { + return; + } + + const isShorthand = typeof selector === 'string'; + + // If we have a selector object instead of shorthand process it. + if ( ! isShorthand ) { + Object.entries( selector ).forEach( + ( [ subfeature, subfeatureSelector ] ) => { + // Don't process root feature selector yet or any + // subfeature that doesn't have a style. + if ( + subfeature === 'root' || + ! styles?.[ feature ][ subfeature ] + ) { + return; + } + + // Create a temporary styles object and build + // declarations for subfeature. + const subfeatureStyles = { + [ feature ]: { + [ subfeature ]: styles[ feature ][ subfeature ], + }, + }; + const newDeclarations = + getStylesDeclarations( subfeatureStyles ); + + // Merge new declarations in with any others that + // share the same selector. + declarations[ subfeatureSelector ] = [ + ...( declarations[ subfeatureSelector ] || [] ), + ...newDeclarations, + ]; + + // Remove the subfeature's style now it will be + // included under its own selector not the block's. + delete styles[ feature ][ subfeature ]; + } + ); + } + + // Now subfeatures have been processed and removed, we can + // process root, or shorthand, feature selectors. + if ( isShorthand || selector.root ) { + const featureSelector = isShorthand ? selector : selector.root; + + // Create temporary style object and build declarations for feature. + const featureStyles = { [ feature ]: styles[ feature ] }; + const newDeclarations = getStylesDeclarations( featureStyles ); + + // Merge new declarations with any others that share the selector. + declarations[ featureSelector ] = [ + ...( declarations[ featureSelector ] || [] ), + ...newDeclarations, + ]; + + // Remove the feature from the block's styles now as it will be + // included under its own selector not the block's. + delete styles[ feature ]; + } + } ); + + return declarations; +}; + /** * Transform given style tree into a set of style declarations. * @@ -692,23 +776,16 @@ export const toStyles = ( // Process styles for block support features with custom feature level // CSS selectors set. if ( featureSelectors ) { - Object.entries( featureSelectors ).forEach( - ( [ featureName, featureSelector ] ) => { - if ( styles?.[ featureName ] ) { - const featureStyles = { - [ featureName ]: styles[ featureName ], - }; - const featureDeclarations = - getStylesDeclarations( featureStyles ); - delete styles[ featureName ]; - - if ( !! featureDeclarations.length ) { - ruleset = - ruleset + - `${ featureSelector }{${ featureDeclarations.join( - ';' - ) } }`; - } + const featureDeclarations = getFeatureDeclarations( + featureSelectors, + styles + ); + + Object.entries( featureDeclarations ).forEach( + ( [ cssSelector, declarations ] ) => { + if ( !! declarations.length ) { + const rules = declarations.join( ';' ); + ruleset = ruleset + `${ cssSelector }{${ rules }}`; } } ); @@ -720,43 +797,32 @@ export const toStyles = ( if ( styles?.variations?.[ styleVariationName ] ) { // If the block uses any custom selectors for block support, add those first. if ( featureSelectors ) { - Object.entries( featureSelectors ).forEach( - ( [ featureName, featureSelector ] ) => { - if ( - styles?.variations?.[ - styleVariationName - ]?.[ featureName ] - ) { - const featureStyles = { - [ featureName ]: - styles.variations[ - styleVariationName - ][ featureName ], - }; - const featureDeclarations = - getStylesDeclarations( - featureStyles + const featureDeclarations = + getFeatureDeclarations( + featureSelectors, + styles?.variations?.[ + styleVariationName + ] + ); + + Object.entries( featureDeclarations ).forEach( + ( [ baseSelector, declarations ] ) => { + if ( !! declarations.length ) { + const cssSelector = + concatFeatureVariationSelectorString( + baseSelector, + styleVariationSelector ); - delete styles.variations[ - styleVariationName - ][ featureName ]; - - if ( - !! featureDeclarations.length - ) { - ruleset = - ruleset + - `${ concatFeatureVariationSelectorString( - featureSelector, - styleVariationSelector - ) }{${ featureDeclarations.join( - ';' - ) } }`; - } + const rules = + declarations.join( ';' ); + ruleset = + ruleset + + `${ cssSelector }{${ rules }}`; } } ); } + // Otherwise add regular selectors. const styleVariationDeclarations = getStylesDeclarations( @@ -907,15 +973,37 @@ export function toSvgFilters( tree, blockSelectors ) { } ); } +const getSelectorsConfig = ( blockType, rootSelector ) => { + if ( ! isEmpty( blockType?.selectors ) ) { + return blockType.selectors; + } + + const config = { root: rootSelector }; + Object.entries( BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS ).forEach( + ( [ featureKey, featureName ] ) => { + const featureSelector = getBlockCSSSelector( + blockType, + featureKey + ); + + if ( featureSelector ) { + config[ featureName ] = featureSelector; + } + } + ); + + return config; +}; + export const getBlockSelectors = ( blockTypes, getBlockStyles ) => { const result = {}; blockTypes.forEach( ( blockType ) => { const name = blockType.name; - const selector = - blockType?.supports?.__experimentalSelector ?? - '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' ); - const duotoneSelector = - blockType?.supports?.color?.__experimentalDuotone ?? null; + const selector = getBlockCSSSelector( blockType, 'root' ); + const duotoneSelector = getBlockCSSSelector( + blockType, + 'filter.duotone' + ); const hasLayoutSupport = !! blockType?.supports?.__experimentalLayout; const fallbackGapValue = blockType?.supports?.spacing?.blockGap?.__experimentalDefault; @@ -930,20 +1018,7 @@ export const getBlockSelectors = ( blockTypes, getBlockStyles ) => { } ); } // For each block support feature add any custom selectors. - const featureSelectors = {}; - Object.entries( BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS ).forEach( - ( [ featureKey, featureName ] ) => { - const featureSelector = - blockType?.supports?.[ featureKey ]?.__experimentalSelector; - - if ( featureSelector ) { - featureSelectors[ featureName ] = scopeSelector( - selector, - featureSelector - ); - } - } - ); + const featureSelectors = getSelectorsConfig( blockType, selector ); result[ name ] = { duotoneSelector, diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index b56d9f64b4e83..a05e188d507e1 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -8,7 +8,11 @@ import namesPlugin from 'colord/plugins/names'; /** * WordPress dependencies */ -import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; +import { + getBlockSupport, + getBlockType, + hasBlockSupport, +} from '@wordpress/blocks'; import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import { useMemo, useContext, createPortal } from '@wordpress/element'; @@ -269,10 +273,9 @@ function BlockDuotoneStyles( { name, duotoneStyle, id } ) { colors = getColorsFromDuotonePreset( colors, duotonePalette ); } - const duotoneSupportSelectors = getBlockSupport( - name, - 'color.__experimentalDuotone' - ); + const duotoneSupportSelectors = + getBlockType( name ).selectors?.filter?.duotone || + getBlockSupport( name, 'color.__experimentalDuotone' ); // Extra .editor-styles-wrapper specificity is needed in the editor // since we're not using inline styles to apply the filter. We need to diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index 2342aa44d6730..b54358fc48d6a 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -85,7 +85,7 @@ "supports": { "anchor": true, "color": { - "__experimentalDuotone": "img, .components-placeholder", + "__experimentalDuotone": true, "text": false, "background": false }, @@ -102,10 +102,10 @@ } }, "selectors": { - "border": ".wp-block-image img" - }, - "editorSelectors": { - "border": ".wp-block-image img, .wp-block-image .wp-block-image__crop-area" + "border": ".wp-block-image img, .wp-block-image .wp-block-image__crop-area", + "filter": { + "duotone": "img, .components-placeholder" + } }, "styles": [ { diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index cf27e0b2f3829..597081f6203e0 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -169,17 +169,9 @@ export function unstable__bootstrapServerSideBlockDefinitions( definitions ) { serverSideBlockDefinitions[ blockName ].ancestor = definitions[ blockName ].ancestor; } - // The `selectors` and `editorSelectors` props are not yet included - // in the server provided definitions. Polyfill it as well. This can - // be removed when the minimum supported WordPress is >= 6.3. - if ( - serverSideBlockDefinitions[ blockName ].editorSelectors === - undefined && - definitions[ blockName ].editorSelectors - ) { - serverSideBlockDefinitions[ blockName ].editorSelectors = - definitions[ blockName ].editorSelectors; - } + // The `selectors` prop is not yet included in the server provided + // definitions. Polyfill it as well. This can be removed when the + // minimum supported WordPress is >= 6.3. if ( serverSideBlockDefinitions[ blockName ].selectors === undefined && @@ -223,7 +215,6 @@ function getBlockSettingsFromMetadata( { textdomain, ...metadata } ) { 'providesContext', 'usesContext', 'selectors', - 'editorSelectors', 'supports', 'styles', 'example', @@ -312,7 +303,6 @@ export function registerBlockType( blockNameOrMetadata, settings ) { providesContext: {}, usesContext: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], diff --git a/packages/blocks/src/api/test/registration.js b/packages/blocks/src/api/test/registration.js index 8a3e9fad0d5b5..dbb11e4d23001 100644 --- a/packages/blocks/src/api/test/registration.js +++ b/packages/blocks/src/api/test/registration.js @@ -135,7 +135,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -283,7 +282,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -321,7 +319,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -355,7 +352,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -391,7 +387,6 @@ describe( 'blocks', () => { usesContext: [ 'textColor' ], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -429,7 +424,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -467,7 +461,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -485,7 +478,6 @@ describe( 'blocks', () => { unstable__bootstrapServerSideBlockDefinitions( { [ blockName ]: { selectors: { root: '.wp-block-custom-selector' }, - editorSelectors: { root: '.editor-only-selector' }, category: 'ignored', }, } ); @@ -505,7 +497,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: { root: '.wp-block-custom-selector' }, - editorSelectors: { root: '.editor-only-selector' }, supports: {}, styles: [], variations: [], @@ -575,7 +566,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -608,7 +598,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -655,7 +644,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -716,7 +704,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -744,7 +731,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -832,7 +818,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -973,7 +958,6 @@ describe( 'blocks', () => { providesContext: {}, usesContext: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [ @@ -1041,7 +1025,6 @@ describe( 'blocks', () => { providesContext: {}, usesContext: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [ { @@ -1108,7 +1091,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -1127,7 +1109,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -1208,7 +1189,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -1235,7 +1215,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -1269,7 +1248,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], @@ -1286,7 +1264,6 @@ describe( 'blocks', () => { usesContext: [], keywords: [], selectors: {}, - editorSelectors: {}, supports: {}, styles: [], variations: [], diff --git a/phpunit/class-wp-get-block-css-selectors-test.php b/phpunit/class-wp-get-block-css-selectors-test.php index 47e42158ceea4..ba63f042a68dc 100644 --- a/phpunit/class-wp-get-block-css-selectors-test.php +++ b/phpunit/class-wp-get-block-css-selectors-test.php @@ -16,30 +16,23 @@ public function set_up() { public function tear_down() { unregister_block_type( $this->test_block_name ); $this->test_block_name = null; - set_current_screen( '' ); parent::tear_down(); } - private function register_test_block( $name, $selectors = null, $supports = null, $editor_selectors = null ) { + private function register_test_block( $name, $selectors = null, $supports = null ) { $this->test_block_name = $name; return register_block_type( $this->test_block_name, array( - 'api_version' => 2, - 'attributes' => array(), - 'selectors' => $selectors, - 'editor_selectors' => $editor_selectors, - 'supports' => $supports, + 'api_version' => 2, + 'attributes' => array(), + 'selectors' => $selectors, + 'supports' => $supports, ) ); } - private function set_screen_to_block_editor() { - set_current_screen( 'edit-post' ); - get_current_screen()->is_block_editor( true ); - } - public function test_get_root_selector_via_selectors_api() { $block_type = self::register_test_block( 'test/block-with-selectors', @@ -87,12 +80,12 @@ public function test_get_duotone_selector_via_selectors_api() { $block_type = self::register_test_block( 'test/duotone-selector', array( - 'filters' => array( 'duotone' => '.duotone-selector' ), + 'filter' => array( 'duotone' => '.duotone-selector' ), ), null ); - $selector = wp_get_block_css_selector( $block_type, array( 'filters', 'duotone' ) ); + $selector = wp_get_block_css_selector( $block_type, array( 'filter', 'duotone' ) ); $this->assertEquals( '.duotone-selector', $selector ); } @@ -107,7 +100,7 @@ public function test_get_duotone_selector_via_experimental_property() { ) ); - $selector = wp_get_block_css_selector( $block_type, 'filters.duotone' ); + $selector = wp_get_block_css_selector( $block_type, 'filter.duotone' ); $this->assertEquals( '.experimental-duotone', $selector ); } @@ -118,7 +111,7 @@ public function test_no_duotone_selector_set() { null ); - $selector = wp_get_block_css_selector( $block_type, 'filters.duotone' ); + $selector = wp_get_block_css_selector( $block_type, 'filter.duotone' ); $this->assertEquals( null, $selector ); } @@ -335,106 +328,4 @@ public function test_string_targets_for_subfeatures() { $selector = wp_get_block_css_selector( $block_type, array( 'typography', 'fontSize' ) ); $this->assertEquals( '.found', $selector ); } - - public function test_editor_only_root_selector() { - self::set_screen_to_block_editor(); - - $block_type = self::register_test_block( - 'test/editor-only-selectors', - array( 'root' => '.wp-custom-block-class' ), - null, - array( 'root' => '.editor-only.wp-custom-block-class' ) - ); - - $selector = wp_get_block_css_selector( $block_type, 'root' ); - $this->assertEquals( '.editor-only.wp-custom-block-class', $selector ); - } - - public function test_editor_only_duotone_selector() { - self::set_screen_to_block_editor(); - - $block_type = self::register_test_block( - 'test/editor-duotone-selector', - array( - 'filters' => array( 'duotone' => '.duotone-selector' ), - ), - null, - array( - 'filters' => array( 'duotone' => '.editor-duotone-selector' ), - ) - ); - - $selector = wp_get_block_css_selector( $block_type, 'filters.duotone' ); - $this->assertEquals( '.editor-duotone-selector', $selector ); - } - - public function test_editor_only_feature_selector() { - self::set_screen_to_block_editor(); - - $block_type = self::register_test_block( - 'test/editor-feature-selector', - array( 'typography' => array( 'root' => '.typography' ) ), - null, - array( 'typography' => array( 'root' => '.editor-typography' ) ) - ); - - $selector = wp_get_block_css_selector( $block_type, 'typography' ); - $this->assertEquals( '.editor-typography', $selector ); - } - - public function test_editor_only_feature_selector_shorthand() { - self::set_screen_to_block_editor(); - - $block_type = self::register_test_block( - 'test/editor-feature-selector', - array( 'typography' => '.typography' ), - null, - array( 'typography' => '.editor-typography' ) - ); - - $selector = wp_get_block_css_selector( $block_type, 'typography' ); - $this->assertEquals( '.editor-typography', $selector ); - } - - public function test_editor_only_subfeature_selector() { - self::set_screen_to_block_editor(); - - $block_type = self::register_test_block( - 'test/editor-subfeature-selector', - array( 'typography' => array( 'fontSize' => '.font-size' ) ), - null, - array( 'typography' => array( 'fontSize' => '.editor-font-size' ) ) - ); - - $selector = wp_get_block_css_selector( $block_type, 'typography.fontSize' ); - $this->assertEquals( '.editor-font-size', $selector ); - } - - public function test_non_editor_subfeature_does_not_fall_back_to_editor_only_feature_selector() { - self::set_screen_to_block_editor(); - - $block_type = self::register_test_block( - 'test/editor-subfeature-selector', - array( 'typography' => array( 'fontSize' => '.font-size' ) ), - null, - array( 'typography' => '.editor-font-size' ) - ); - - $selector = wp_get_block_css_selector( $block_type, 'typography.fontSize', true ); - $this->assertEquals( '.font-size', $selector ); - } - - public function test_unspecified_subfeature_falls_back_to_editor_only_feature_selector() { - self::set_screen_to_block_editor(); - - $block_type = self::register_test_block( - 'test/editor-subfeature-selector', - array( 'typography' => '.typography' ), - null, - array( 'typography' => '.editor-typography' ) - ); - - $selector = wp_get_block_css_selector( $block_type, 'typography.fontSize', true ); - $this->assertEquals( '.editor-typography', $selector ); - } } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 104d87afc3e39..eeb5940f44cd3 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -460,7 +460,7 @@ public function test_get_stylesheet() { ); $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; - $styles = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;min-height: 50vh;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}.wp-block-heading{color: #123456;}.wp-block-heading a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-post-excerpt{column-count: 2;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}'; + $styles = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;min-height: 50vh;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}.wp-block-heading{color: #123456;}.wp-block-heading a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-post-excerpt{column-count: 2;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}'; $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}'; $all = $variables . $styles . $presets; diff --git a/schemas/json/block.json b/schemas/json/block.json index c0da49776b3f7..6023d829c5ecd 100644 --- a/schemas/json/block.json +++ b/schemas/json/block.json @@ -526,104 +526,6 @@ } } }, - "editorSelectors": { - "type": "object", - "description": "Provides editor specific overrides to the custom CSS selectors defined within the block's selectors config.", - "properties": { - "root": { - "type": "string", - "description": "The primary CSS class to apply to the block within the editor. This replaces the `.wp-block-name` class if set." - }, - "border": { - "description": "Custom CSS selector used to generate rules for the block's theme.json border styles within the editor.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "root": { "type": "string" }, - "color": { "type": "string" }, - "radius": { "type": "string" }, - "style": { "type": "string" }, - "width": { "type": "string" } - } - } - ] - }, - "color": { - "description": "Custom CSS selector used to generate rules for the block's theme.json color styles within the editor.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "root": { "type": "string" }, - "text": { "type": "string" }, - "background": { "type": "string" } - } - } - ] - }, - "dimensions": { - "description": "Custom CSS selector used to generate rules for the block's theme.json dimensions styles within the editor.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "root": { "type": "string" }, - "minHeight": { "type": "string" } - } - } - ] - }, - "spacing": { - "description": "Custom CSS selector used to generate rules for the block's theme.json spacing styles within the editor.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "root": { "type": "string" }, - "blockGap": { "type": "string" }, - "padding": { "type": "string" }, - "margin": { "type": "string" } - } - } - ] - }, - "typography": { - "description": "Custom CSS selector used to generate rules for the block's theme.json typography styles within the editor.", - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "root": { "type": "string" }, - "fontFamily": { "type": "string" }, - "fontSize": { "type": "string" }, - "fontStyle": { "type": "string" }, - "fontWeight": { "type": "string" }, - "lineHeight": { "type": "string" }, - "letterSpacing": { "type": "string" }, - "textDecoration": { "type": "string" }, - "textTransform": { "type": "string" } - } - } - ] - } - } - }, "styles": { "type": "array", "description": "Block styles can be used to provide alternative styles to block. It works by adding a class name to the block’s wrapper. Using CSS, a theme developer can target the class name for the block style if it is selected.\n\nPlugins and Themes can also register custom block style for existing blocks.\n\nhttps://developer.wordpress.org/block-editor/reference-guides/block-api/block-styles",