Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selectors API: Make duotone selectors fallback and be scoped #49423

Merged
merged 33 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7735980
Make duotone selectors fallback and be scoped
aaronrobertshaw Mar 29, 2023
0c52d64
Use whole selectors for duotone as well
oandregal Mar 29, 2023
02bfc17
Update the block-level duotone selector
oandregal Mar 29, 2023
e5ed227
Use existing function
oandregal Mar 29, 2023
e60c4b7
scope the selectors in the post editor
scruffian Mar 29, 2023
555f7d0
Duotone: do not scope twice
oandregal Mar 29, 2023
64a505a
Simplify __experimentalDuotone backwards compatibility
Mar 29, 2023
3cd86f0
Update the __experimentalDuotoen back compat to explicitly handle eac…
Mar 29, 2023
7d370c6
Simplify adding the filter class to the selectors
Mar 29, 2023
521db54
Refactor to handle __experimentalDuotone where it was previously handled
Mar 30, 2023
fc2f0c0
Remove some dead code that I missed
Mar 30, 2023
53410e5
Fix lint
Mar 30, 2023
42e1b3f
Remove __experimentalDuotone test since it doesn't handle that anymore
Mar 30, 2023
50997ca
Remove remainder of duotone tests because it is no longer handled dif…
Mar 30, 2023
b9d16ea
Fix getBlockSelectors test because __experimentalDuotone now always r…
Mar 30, 2023
31aa06e
Fix rendering when preset doesn't exist
Mar 30, 2023
114ec2b
Fix unset filters
Mar 30, 2023
da60b58
Fix typos
aaronrobertshaw Mar 30, 2023
6a750e3
Update docs
aaronrobertshaw Mar 30, 2023
2236ce5
Tweak incorrect comment
aaronrobertshaw Mar 30, 2023
972dca7
Fix duotone support flag relocation
aaronrobertshaw Mar 30, 2023
b401c12
Add filter.duotone to block-supports docs
aaronrobertshaw Mar 30, 2023
59b135e
Add filter.duotone to block.json schema
aaronrobertshaw Mar 30, 2023
6367140
Be more precise in filterId selector comment
Mar 30, 2023
f1506d0
Fix filters in editor applying to cover block text
Mar 30, 2023
502f8b4
Remove comment I accidentally committed
Mar 30, 2023
571681e
Remove __experimentalDuotone from color supports docs
Mar 30, 2023
5f16e9c
Add example to the filter.duotone docs
Mar 30, 2023
bd02cb2
Move migration into duotone class
Mar 30, 2023
b20c08e
Add deprecation note to experimentalDuotone docs
Mar 30, 2023
a7337a7
Fix PHP lint
Mar 30, 2023
7e7bf1c
Fix PHP lint
Mar 30, 2023
3346b9a
Correct experimental BC comments and tweak flow
aaronrobertshaw Mar 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 56 additions & 40 deletions docs/reference-guides/block-api/block-supports.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ supports: {
- Default value: null
- Subproperties:
- `background`: type `boolean`, default value `true`
- `__experimentalDuotone`: type `string`, default value undefined
- `gradients`: type `boolean`, default value `false`
- `link`: type `boolean`, default value `false`
- `text`: type `boolean`, default value `true`
Expand Down Expand Up @@ -231,46 +230,9 @@ When the block declares support for `color.background`, the attributes definitio

### color.__experimentalDuotone
ajlende marked this conversation as resolved.
Show resolved Hide resolved

This property adds UI controls which allow to apply a duotone filter to a block or part of a block.
_**Note:** Deprecated since WordPress 6.3._

The parent selector is automatically added much like nesting in Sass/SCSS (however, the `&` selector is not supported).

```js
supports: {
color: {
// Apply the filter to the same selector in both edit and save.
__experimentalDuotone: '> .duotone-img, > .duotone-video',

// Default values must be disabled if you don't want to use them with duotone.
background: false,
text: false
}
}
```

Duotone presets are sourced from `color.duotone` in [theme.json](/docs/how-to-guides/themes/theme-json.md).

When the block declares support for `color.__experimentalDuotone`, the attributes definition is extended to include the attribute `style`:

- `style`: attribute of `object` type with no default assigned.

The block can apply a default duotone color by specifying its own attribute with a default e.g.:

```js
attributes: {
style: {
type: 'object',
default: {
color: {
duotone: [
'#FFF',
'#000'
]
}
}
}
}
```
This property has been replaced by [`filter.duotone`](#filter-duotone).

### color.gradients

Expand Down Expand Up @@ -499,6 +461,60 @@ attributes: {
}
```

## filter
- Type: `Object`
- Default value: null
- Subproperties:
- `duotone`: type `boolean`, default value `false`

This value signals that a block supports some of the properties related to filters. When it does, the block editor will show UI controls for the user to set their values.

### filter.duotone

This property adds UI controls which allow the user to apply a duotone filter to
a block or part of a block.

```js
supports: {
filter: {
// Enable duotone support
duotone: true
}
},
selectors: {
filter: {
// Apply the filter to img elements inside the image block
duotone: '.wp-block-image img'
}
}
```

The filter can be applied to an element inside the block by setting the `selectors.filter.duotone` selector.

Duotone presets are sourced from `color.duotone` in [theme.json](/docs/how-to-guides/themes/theme-json.md).

When the block declares support for `filter.duotone`, the attributes definition is extended to include the attribute `style`:

- `style`: attribute of `object` type with no default assigned.

The block can apply a default duotone color by specifying its own attribute with a default e.g.:

```js
attributes: {
style: {
type: 'object',
default: {
color: {
duotone: [
'#FFF',
'#000'
]
}
}
}
}
```

## html

- Type: `boolean`
Expand Down
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ Insert an image to make a visual statement. ([Source](https://github.com/WordPre

- **Name:** core/image
- **Category:** media
- **Supports:** anchor, color (~~background~~, ~~text~~)
- **Supports:** anchor, color (~~background~~, ~~text~~), filter (duotone)
- **Attributes:** align, alt, caption, height, href, id, linkClass, linkDestination, linkTarget, rel, sizeSlug, title, url, width

## Latest Comments
Expand Down
5 changes: 4 additions & 1 deletion lib/block-supports/duotone.php
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,9 @@ function gutenberg_get_duotone_filter_svg( $preset ) {
function gutenberg_register_duotone_support( $block_type ) {
$has_duotone_support = false;
if ( property_exists( $block_type, 'supports' ) ) {
$has_duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
// Previous `color.__experimentalDuotone` support flag is migrated
// to `filter.duotone` via `block_type_metadata_settings` filter.
$has_duotone_support = _wp_array_get( $block_type->supports, array( 'filter', 'duotone' ), null );
}

if ( $has_duotone_support ) {
Expand Down Expand Up @@ -450,3 +452,4 @@ function gutenberg_render_duotone_support( $block_content, $block ) {
add_action( 'wp_enqueue_scripts', array( 'WP_Duotone_Gutenberg', 'output_global_styles' ), 11 );
add_action( 'wp_footer', array( 'WP_Duotone_Gutenberg', 'output_footer_assets' ), 10 );
add_filter( 'block_editor_settings_all', array( 'WP_Duotone_Gutenberg', 'add_editor_settings' ), 10 );
add_filter( 'block_type_metadata_settings', array( 'WP_Duotone_Gutenberg', 'migrate_experimental_duotone_support_flag' ), 10, 2 );
78 changes: 68 additions & 10 deletions lib/class-wp-duotone-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,42 @@ public static function output_global_styles() {
}
}

/**
* Get the CSS selector for a block type.
*
* @param string $block_name The block name.
*
* @return string The CSS selector or null if there is no support.
*/
private static function get_selector( $block_name ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );

if ( $block_type && property_exists( $block_type, 'supports' ) ) {
// Backwards compatibility with `supports.color.__experimentalDuotone`
// is provided via the `block_type_metadata_settings` filter. If
// `supports.filter.duotone` has not been set and the experimental
// property has been, the experimental property value is copied into
// `supports.filter.duotone`.
$duotone_support = _wp_array_get( $block_type->supports, array( 'filter', 'duotone' ), false );
if ( ! $duotone_support ) {
return null;
}

// If the experimental duotone support was set, that value is to be
// treated as a selector and requires scoping.
$experimental_duotone = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), false );
if ( $experimental_duotone ) {
$root_selector = wp_get_block_css_selector( $block_type );
return is_string( $experimental_duotone )
? WP_Theme_JSON_Gutenberg::scope_selector( $root_selector, $experimental_duotone )
: $root_selector;
}

// Regular filter.duotone support uses filter.duotone selectors with fallbacks.
return wp_get_block_css_selector( $block_type, array( 'filter', 'duotone' ), true );
}
}

/**
* Render out the duotone CSS styles and SVG.
*
Expand All @@ -272,22 +308,15 @@ public static function output_global_styles() {
* @return string Filtered block content.
*/
public static function render_duotone_support( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );

$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;
}
$duotone_selector = self::get_selector( $block['blockName'] );

// The block should have a duotone attribute or have duotone defined in its theme.json to be processed.
$has_duotone_attribute = isset( $block['attrs']['style']['color']['duotone'] );
$has_global_styles_duotone = array_key_exists( $block['blockName'], self::$global_styles_block_names );

if (
empty( $block_content ) ||
! $duotone_support ||
! $duotone_selector ||
( ! $has_duotone_attribute && ! $has_global_styles_duotone )
) {
return $block_content;
Expand Down Expand Up @@ -349,7 +378,17 @@ 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_selector );
$selectors = explode( ',', $duotone_selector );

$selectors_scoped = array();
foreach ( $selectors as $selector_part ) {
// Assuming the selector part is a subclass selector (not a tag name)
// so we can prepend the filter id class. If we want to support elements
// such as `img` or namespaces, we'll need to add a case for that here.
$selectors_scoped[] = '.' . $filter_id . trim( $selector_part );
}

$selector = implode( ', ', $selectors_scoped );

// 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 ) ) {
Expand Down Expand Up @@ -386,4 +425,23 @@ public static function render_duotone_support( $block_content, $block ) {

return $tags->get_updated_html();
}

/**
* Migrate the old experimental duotone support flag to its stabilized location
* under `supports.filter.duotone` and sets.
*
* @param array $settings Current block type settings.
* @param array $metadata Block metadata as read in via block.json.
*
* @return array Filtered block type settings.
*/
public static function migrate_experimental_duotone_support_flag( $settings, $metadata ) {
$duotone_support = _wp_array_get( $metadata, array( 'supports', 'color', '__experimentalDuotone' ), null );

if ( ! isset( $settings['supports']['filter']['duotone'] ) && null !== $duotone_support ) {
_wp_array_set( $settings, array( 'supports', 'filter', 'duotone' ), (bool) $duotone_support );
}

return $settings;
}
}
14 changes: 12 additions & 2 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,17 @@ 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, 'filter.duotone' );

// Keep backwards compatibility for support.color.__experimentalDuotone.
if ( null === $duotone_selector ) {
$duotone_support = _wp_array_get( $block_type->supports, array( 'color', '__experimentalDuotone' ), null );

if ( $duotone_support ) {
$root_selector = wp_get_block_css_selector( $block_type );
$duotone_selector = WP_Theme_JSON_Gutenberg::scope_selector( $root_selector, $duotone_support );
}
}

if ( null !== $duotone_selector ) {
static::$blocks_metadata[ $block_name ]['duotone'] = $duotone_selector;
}
Expand Down Expand Up @@ -2389,8 +2400,7 @@ function( $pseudo_selector ) use ( $selector ) {

// 3. Generate and append the rules that use the duotone selector.
if ( isset( $block_metadata['duotone'] ) && ! empty( $declarations_duotone ) ) {
$selector_duotone = static::scope_selector( $block_metadata['selector'], $block_metadata['duotone'] );
$block_rules .= static::to_ruleset( $selector_duotone, $declarations_duotone );
$block_rules .= static::to_ruleset( $block_metadata['duotone'], $declarations_duotone );
aaronrobertshaw marked this conversation as resolved.
Show resolved Hide resolved
}

// 4. Generate Layout block gap styles.
Expand Down
35 changes: 3 additions & 32 deletions lib/compat/wordpress-6.3/get-global-styles-and-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,6 @@ 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
Expand All @@ -59,8 +48,8 @@ function wp_get_block_css_selector( $block_type, $target = 'root', $fallback = f
return $root_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 target is not `root` 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 );
}
Expand Down Expand Up @@ -95,25 +84,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 WP_Theme_JSON_Gutenberg::scope_selector( $root_selector, $feature_selector );
}

// Subfeature selector
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@ 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ describe( 'global styles renderer', () => {
'core/image': {
name: imageBlock.name,
selector: imageSupports.__experimentalSelector,
duotoneSelector: imageSupports.color.__experimentalDuotone,
duotoneSelector: '.my-image img',
fallbackGapValue: undefined,
featureSelectors: {
root: '.my-image',
Expand Down
Loading