From 520ea49e931f802138f7903d9e82b7e477fd4ceb Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Wed, 29 Mar 2023 17:14:34 +1000 Subject: [PATCH] Make duotone selectors fallback and be scoped --- .../get-global-styles-and-settings.php | 84 +++++++++++-------- .../global-styles/get-block-css-selector.js | 42 ++++++---- .../global-styles/use-global-styles-output.js | 9 +- packages/block-editor/src/hooks/duotone.js | 8 +- .../class-wp-get-block-css-selectors-test.php | 24 +++++- 5 files changed, 104 insertions(+), 63 deletions(-) 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 c213e50d64ae7b..5effd6718e64cd 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 @@ -25,21 +25,7 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f $has_selectors = ! empty( $block_type->selectors ); - // Duotone (No fallback selectors for Duotone). - 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( 'filter', 'duotone' ), null ); - } - - // Selectors API, not available, check for old experimental selector. - return _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), null ); - } - - // Root Selector. - - // Calculated before returning as it can be used as fallback for - // feature selectors later on. + // Root Selector ( can be used as a fallback ). $root_selector = null; if ( $has_selectors && isset( $block_type->selectors['root'] ) ) { @@ -59,16 +45,58 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f return $root_selector; } + $fallback_selector = $fallback ? $root_selector : null; + + // Helper to scope old experimental selectors. + $scope_selector = function( $scope, $selector ) { + $scopes = explode( ',', $scope ); + $selectors = explode( ',', $selector ); + + $selectors_scoped = array(); + foreach ( $scopes as $outer ) { + foreach ( $selectors as $inner ) { + $outer = trim( $outer ); + $inner = trim( $inner ); + if ( ! empty( $outer ) && ! empty( $inner ) ) { + $selectors_scoped[] = $outer . ' ' . $inner; + } elseif ( empty( $outer ) ) { + $selectors_scoped[] = $inner; + } elseif ( empty( $inner ) ) { + $selectors_scoped[] = $outer; + } + } + } + + return implode( ', ', $selectors_scoped ); + }; + + // Duotone ( may fallback to root selector ). + if ( 'filter.duotone' === $target || array( 'filter', 'duotone' ) === $target ) { + // If selectors API in use, only use it's value, fallback, or null. + if ( $has_selectors ) { + return _wp_array_get( $block_type->selectors, array( 'filter', 'duotone' ), $fallback_selector ); + } + + // Selectors API, not available, check for old experimental selector. + $duotone_selector = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), null ); + + // Nothing to work with, provide fallback or null. + if ( null === $duotone_selector ) { + return $fallback_selector; + } + + // Scope the duotone selector by the block's root selector. + return $scope_selector( $root_selector, $duotone_selector ); + } + // 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. if ( is_string( $target ) ) { $target = explode( '.', $target ); } - // Feature Selectors ( May fallback to root selector ). + // Feature Selectors ( may fallback to root selector ). if ( 1 === count( $target ) ) { - $fallback_selector = $fallback ? $root_selector : null; - // Prefer the selectors API if available. if ( $has_selectors ) { // Look for selector under `feature.root`. @@ -95,25 +123,7 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f } // Scope the feature selector by the block's root selector. - $scopes = explode( ',', $root_selector ); - $selectors = explode( ',', $feature_selector ); - - $selectors_scoped = array(); - foreach ( $scopes as $outer ) { - foreach ( $selectors as $inner ) { - $outer = trim( $outer ); - $inner = trim( $inner ); - if ( ! empty( $outer ) && ! empty( $inner ) ) { - $selectors_scoped[] = $outer . ' ' . $inner; - } elseif ( empty( $outer ) ) { - $selectors_scoped[] = $inner; - } elseif ( empty( $inner ) ) { - $selectors_scoped[] = $outer; - } - } - } - - return implode( ', ', $selectors_scoped ); + return $scope_selector( $root_selector, $feature_selector ); } // Subfeature selector 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 index db58709fe79aae..76a56f88fc34a4 100644 --- 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 @@ -34,21 +34,7 @@ export function getBlockCSSSelector( 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. + // Root selector ( can be used as fallback ). let rootSelector = null; if ( hasSelectors && selectors.root ) { @@ -68,14 +54,36 @@ export function getBlockCSSSelector( return rootSelector; } + const fallbackSelector = fallback ? rootSelector : null; + + // Duotone ( may fallback to root selector ). + if ( path === 'filter.duotone' ) { + // If selectors API in use, only use its value, fallback, or null. + if ( hasSelectors ) { + return get( selectors, path, fallbackSelector ); + } + + // Selectors API, not available, check for old experimental selector. + const duotoneSelector = get( + supports, + 'color.__experimentalDuotone', + null + ); + + // If nothing to work with, provide fallback selector if available. + if ( ! duotoneSelector ) { + return fallbackSelector; + } + + return scopeSelector( rootSelector, duotoneSelector ); + } + // 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. 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 20fa92e843f314..fe32f72ea9ff88 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 @@ -19,7 +19,7 @@ import { getCSSRules } from '@wordpress/style-engine'; /** * Internal dependencies */ -import { PRESET_METADATA, ROOT_BLOCK_SELECTOR, scopeSelector } from './utils'; +import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils'; import { getBlockCSSSelector } from './get-block-css-selector'; import { getTypographyFontSizeValue } from './typography-utils'; import { GlobalStylesContext } from './context'; @@ -856,10 +856,9 @@ export const toStyles = ( if ( duotoneDeclarations.length > 0 ) { ruleset = ruleset + - `${ scopeSelector( - selector, - duotoneSelector - ) }{${ duotoneDeclarations.join( ';' ) };}`; + `${ duotoneSelector }{${ duotoneDeclarations.join( + ';' + ) };}`; } } diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index a05e188d507e1a..fb7a749d82f7e6 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -32,6 +32,7 @@ import { __unstableDuotoneStylesheet as DuotoneStylesheet, __unstableDuotoneUnsetStylesheet as DuotoneUnsetStylesheet, } from '../components/duotone'; +import { getBlockCSSSelector } from '../components/global-styles/get-block-css-selector'; import { store as blockEditorStore } from '../store'; const EMPTY_ARRAY = []; @@ -273,9 +274,10 @@ function BlockDuotoneStyles( { name, duotoneStyle, id } ) { colors = getColorsFromDuotonePreset( colors, duotonePalette ); } - const duotoneSupportSelectors = - getBlockType( name ).selectors?.filter?.duotone || - getBlockSupport( name, 'color.__experimentalDuotone' ); + const duotoneSupportSelectors = getBlockCSSSelector( + getBlockType( name ), + 'filter.duotone' + ); // 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/phpunit/class-wp-get-block-css-selectors-test.php b/phpunit/class-wp-get-block-css-selectors-test.php index ba63f042a68dc0..7d3c9889e8a3a4 100644 --- a/phpunit/class-wp-get-block-css-selectors-test.php +++ b/phpunit/class-wp-get-block-css-selectors-test.php @@ -101,7 +101,7 @@ public function test_get_duotone_selector_via_experimental_property() { ); $selector = wp_get_block_css_selector( $block_type, 'filter.duotone' ); - $this->assertEquals( '.experimental-duotone', $selector ); + $this->assertEquals( '.wp-block-test-experimental-duotone-selector .experimental-duotone', $selector ); } public function test_no_duotone_selector_set() { @@ -115,6 +115,28 @@ public function test_no_duotone_selector_set() { $this->assertEquals( null, $selector ); } + public function test_fallback_duotone_selector() { + $block_type = self::register_test_block( + 'test/fallback-duotone-selector', + array( 'root' => '.fallback-root-selector' ), + null + ); + + $selector = wp_get_block_css_selector( $block_type, 'filter.duotone', true ); + $this->assertEquals( '.fallback-root-selector', $selector ); + } + + public function test_fallback_duotone_selector_to_generated_class() { + $block_type = self::register_test_block( + 'test/fallback-duotone-selector', + array(), + null + ); + + $selector = wp_get_block_css_selector( $block_type, 'filter.duotone', true ); + $this->assertEquals( '.wp-block-test-fallback-duotone-selector', $selector ); + } + public function test_get_feature_selector_via_selectors_api() { $block_type = self::register_test_block( 'test/feature-selector',