Skip to content

Commit

Permalink
Global Styles: Allow references to values in other locations in the t…
Browse files Browse the repository at this point in the history
…ree (#41696)

* Elements: Add an API make it easier to get class names (#41753)

* Elements: Add an api make it easier to get class names

* Add some PHP unit tests

* Add a unit tests for elements

* lint fix

* fix PHP tests

* Update lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php

Co-authored-by: George Mamadashvili <[email protected]>

* Add @SInCE

* update form.js

* fix PHP test

Co-authored-by: George Mamadashvili <[email protected]>

Theme JSON: Add dynamic properties

Theme JSON: Make it possible to use styles and settings from elsewhere in the tree

also make button text dynamic

fix type

remove unconnected changes

move the value replacement earlier and add validation

use an object rather than a string to fetch other values

adding a doing it wrong message

remove color changes for buttons

add an explanatory comment

change source to ref

Add unit tests

Update lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php

Co-authored-by: Adam Zielinski <[email protected]>

lint fixes

fix fixtures

remove incorrect change

lint fixes

* fix rebase
  • Loading branch information
scruffian authored Jun 24, 2022
1 parent fde1e30 commit cb1b5fd
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 7 deletions.
121 changes: 117 additions & 4 deletions lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ public static function remove_insecure_properties( $theme_json ) {
return $theme_json;
}


/**
* Returns the metadata for each block.
*
Expand Down Expand Up @@ -451,7 +450,6 @@ private static function get_block_nodes( $theme_json, $selectors = array() ) {
* @return string Styles for the block.
*/
public function get_styles_for_block( $block_metadata ) {

$node = _wp_array_get( $this->theme_json, $block_metadata['path'], array() );

$selector = $block_metadata['selector'];
Expand All @@ -474,9 +472,9 @@ public function get_styles_for_block( $block_metadata ) {
// element then compute the style properties for it.
// Otherwise just compute the styles for the default selector as normal.
if ( $pseudo_selector && isset( $node[ $pseudo_selector ] ) && isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ] ) && in_array( $pseudo_selector, static::VALID_ELEMENT_PSEUDO_SELECTORS[ $current_element ], true ) ) {
$declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings );
$declarations = static::compute_style_properties( $node[ $pseudo_selector ], $settings, null, $this->theme_json );
} else {
$declarations = static::compute_style_properties( $node, $settings );
$declarations = static::compute_style_properties( $node, $settings, null, $this->theme_json );
}

$block_rules = '';
Expand Down Expand Up @@ -554,4 +552,119 @@ protected function get_block_classes( $style_nodes ) {

return $block_rules;
}

/**
* Given a styles array, it extracts the style properties
* and adds them to the $declarations array following the format:
*
* ```php
* array(
* 'name' => 'property_name',
* 'value' => 'property_value,
* )
* ```
*
* @param array $styles Styles to process.
* @param array $settings Theme settings.
* @param array $properties Properties metadata.
* @param array $theme_json Theme JSON array.
* @return array Returns the modified $declarations.
*/
protected static function compute_style_properties( $styles, $settings = array(), $properties = null, $theme_json = null ) {
if ( null === $properties ) {
$properties = static::PROPERTIES_METADATA;
}

$declarations = array();
if ( empty( $styles ) ) {
return $declarations;
}

foreach ( $properties as $css_property => $value_path ) {
$value = static::get_property_value( $styles, $value_path, $theme_json );

// Look up protected properties, keyed by value path.
// Skip protected properties that are explicitly set to `null`.
if ( is_array( $value_path ) ) {
$path_string = implode( '.', $value_path );
if (
array_key_exists( $path_string, static::PROTECTED_PROPERTIES ) &&
_wp_array_get( $settings, static::PROTECTED_PROPERTIES[ $path_string ], null ) === null
) {
continue;
}
}

// Skip if empty and not "0" or value represents array of longhand values.
$has_missing_value = empty( $value ) && ! is_numeric( $value );
if ( $has_missing_value || is_array( $value ) ) {
continue;
}

$declarations[] = array(
'name' => $css_property,
'value' => $value,
);
}

return $declarations;
}

/**
* Returns the style property for the given path.
*
* It also converts CSS Custom Property stored as
* "var:preset|color|secondary" to the form
* "--wp--preset--color--secondary".
*
* It also converts references to a path to the value
* stored at that location, e.g.
* { "ref": "style.color.background" } => "#fff".
*
* @param array $styles Styles subtree.
* @param array $path Which property to process.
* @param array $theme_json Theme JSON array.
* @return string Style property value.
*/
protected static function get_property_value( $styles, $path, $theme_json = null ) {
$value = _wp_array_get( $styles, $path, '' );

// This converts references to a path to the value at that path
// where the values is an array with a "ref" key, pointing to a path.
// For example: { "ref": "style.color.background" } => "#fff".
if ( is_array( $value ) && array_key_exists( 'ref', $value ) ) {
$value_path = explode( '.', $value['ref'] );
$ref_value = _wp_array_get( $theme_json, $value_path );
// Only use the ref value if we find anything.
if ( ! empty( $ref_value ) && is_string( $ref_value ) ) {
$value = $ref_value;
}

if ( is_array( $ref_value ) && array_key_exists( 'ref', $ref_value ) ) {
$path_string = json_encode( $path );
$ref_value_string = json_encode( $ref_value );
_doing_it_wrong( 'get_property_value', "Your theme.json file uses a dynamic value (${ref_value_string}) for the path at ${path_string}. However, the value at ${path_string} is also a dynamic value (pointing to ${ref_value['ref']}) and pointing to another dynamic value is not supported. Please update ${path_string} to point directly to ${ref_value['ref']}.", '6.1.0' );
}
}

if ( '' === $value || is_array( $value ) ) {
return $value;
}

// Convert custom CSS properties.
$prefix = 'var:';
$prefix_len = strlen( $prefix );
$token_in = '|';
$token_out = '--';
if ( 0 === strncmp( $value, $prefix, $prefix_len ) ) {
$unwrapped_name = str_replace(
$token_in,
$token_out,
substr( $value, $prefix_len )
);
$value = "var(--wp--$unwrapped_name)";
}

return $value;
}
}
2 changes: 0 additions & 2 deletions lib/experimental/class-wp-theme-json-resolver-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,4 @@ public static function get_merged_data( $origin = 'custom' ) {

return $result;
}


}
115 changes: 115 additions & 0 deletions phpunit/class-wp-theme-json-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -2821,4 +2821,119 @@ function test_get_element_class_name_invalid() {

$this->assertEquals( $expected, $actual );
}

/**
* Testing that dynamic properties in theme.json return the value they refrence, e.g.
* array( 'ref' => 'styles.color.background' ) => "#ffffff".
*/
function test_get_property_value_valid() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => 2,
'styles' => array(
'color' => array(
'background' => '#ffffff',
'text' => '#000000',
),
'elements' => array(
'button' => array(
'color' => array(
'background' => array( 'ref' => 'styles.color.text' ),
'text' => array( 'ref' => 'styles.color.background' ),
),
),
),
),
)
);

$expected = 'body { margin: 0; }body{background-color: #ffffff;color: #000000;}.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-element-button, .wp-block-button__link{background-color: #000000;color: #ffffff;}';
$this->assertEquals( $expected, $theme_json->get_stylesheet() );
}

/**
* Testing that dynamic properties in theme.json that
* refer to other dynamic properties in a loop
* then they should be left untouched.
*
* @expectedIncorrectUsage get_property_value
*/
function test_get_property_value_loop() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => 2,
'styles' => array(
'color' => array(
'background' => '#ffffff',
'text' => array( 'ref' => 'styles.elements.button.color.background' ),
),
'elements' => array(
'button' => array(
'color' => array(
'background' => array( 'ref' => 'styles.color.text' ),
'text' => array( 'ref' => 'styles.color.background' ),
),
),
),
),
)
);

$expected = 'body { margin: 0; }body{background-color: #ffffff;}.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-element-button, .wp-block-button__link{color: #ffffff;}';
$this->assertSame( $expected, $theme_json->get_stylesheet() );
}

/**
* Testing that dynamic properties in theme.json that
* refer to other dynamic properties then they should be left unprocessed.
*
* @expectedIncorrectUsage get_property_value
*/
function test_get_property_value_recursion() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => 2,
'styles' => array(
'color' => array(
'background' => '#ffffff',
'text' => array( 'ref' => 'styles.color.background' ),
),
'elements' => array(
'button' => array(
'color' => array(
'background' => array( 'ref' => 'styles.color.text' ),
'text' => array( 'ref' => 'styles.color.background' ),
),
),
),
),
)
);

$expected = 'body { margin: 0; }body{background-color: #ffffff;color: #ffffff;}.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-element-button, .wp-block-button__link{color: #ffffff;}';
$this->assertEquals( $expected, $theme_json->get_stylesheet() );
}

/**
* Testing that dynamic properties in theme.json that
* refer to themselves then they should be left unprocessed.
*
* @expectedIncorrectUsage get_property_value
*/
function test_get_property_value_self() {
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => 2,
'styles' => array(
'color' => array(
'background' => '#ffffff',
'text' => array( 'ref' => 'styles.color.text' ),
),
),
)
);

$expected = 'body { margin: 0; }body{background-color: #ffffff;}.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; }';
$this->assertEquals( $expected, $theme_json->get_stylesheet() );
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<!-- wp:table {"backgroundColor":"subtle-pale-blue"} -->
<figure class="wp-block-table"><table class="has-subtle-pale-blue-background-color has-background"><thead><tr><th>Version</th><th>Musician</th><th>Date</th></tr></thead><tbody><tr><td><a href="https://wordpress.org/news/2003/05/wordpress-now-available/">.70</a></td><td>No musician chosen.</td><td>May 27, 2003</td></tr><tr><td><a href="https://wordpress.org/news/2004/01/wordpress-10/">1.0</a></td><td>Miles Davis</td><td>January 3, 2004</td></tr><tr><td>Lots of versions skipped, see <a href="https://codex.wordpress.org/WordPress_Versions">the full list</a></td><td>&hellip;</td><td>&hellip;</td></tr><tr><td><a href="https://wordpress.org/news/2015/12/clifford/">4.4</a></td><td>Clifford Brown</td><td>December 8, 2015</td></tr><tr><td><a href="https://wordpress.org/news/2016/04/coleman/">4.5</a></td><td>Coleman Hawkins</td><td>April 12, 2016</td></tr><tr><td><a href="https://wordpress.org/news/2016/08/pepper/">4.6</a></td><td>Pepper Adams</td><td>August 16, 2016</td></tr><tr><td><a href="https://wordpress.org/news/2016/12/vaughan/">4.7</a></td><td>Sarah Vaughan</td><td>December 6, 2016</td></tr></tbody></table><figcaption>Table Caption</figcaption></figure>
<!-- /wp:table -->
<!-- /wp:table -->

0 comments on commit cb1b5fd

Please sign in to comment.