Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
343fafc
Refactor magic number to constant
ajlende May 21, 2024
21cf2ba
Add defaultSpacingSizes option
ajlende May 30, 2024
119bff1
Add migration docs
ajlende May 22, 2024
f19da26
Update API docs
ajlende May 22, 2024
9ede3bb
Refactor to not need constants
ajlende May 22, 2024
b3f304b
Update PHPUnit tests
ajlende May 22, 2024
666fc4e
Fix PHP lint
ajlende May 22, 2024
0301b49
Fix PHP lint
ajlende May 22, 2024
f5010ef
Make sure all spread vairables cannot be null
ajlende May 22, 2024
6fbb99f
Code cleanup
ajlende May 22, 2024
b1126b7
Add backport-changelog
ajlende May 23, 2024
8a5f339
Prevent spacingSizes from entirely replacing presets generated from s…
ajlende May 29, 2024
73d63a7
Be more specific about limits in the schema
ajlende May 29, 2024
ada604b
Move comment
ajlende May 29, 2024
882f02e
Increase limit to 10
ajlende May 30, 2024
3415b6a
Fix merging of origins
ajlende May 30, 2024
8529aef
Use old when checking for existance in migration
ajlende May 30, 2024
5d2762a
Fix copy/paste mistake
ajlende May 30, 2024
8093334
Fix some v2 backcompat
ajlende May 30, 2024
59eaac3
Don't bother with skipping pre-generation on v2 for filters
ajlende May 30, 2024
1aafb16
Switch back to numbers for labels in the range control
ajlende May 30, 2024
21e6de7
Deprecate set_spacing_sizes
ajlende May 30, 2024
d42932b
Remove comment
ajlende May 30, 2024
c5778d0
Remove incorrect doc param
ajlende May 30, 2024
31ac387
Clarify comment
ajlende May 30, 2024
17db2d4
Add method version docs
ajlende May 30, 2024
25dc819
Fix PHPUnit tests
ajlende May 30, 2024
144dc5a
Fix PHPUnit tests
ajlende May 30, 2024
a592617
Revert to bugged spacing scale check
ajlende May 30, 2024
1a3d0c2
Add comment about bad conditional
ajlende May 30, 2024
12edffa
Do sorting on all presets in UI only
ajlende May 30, 2024
44bf7b8
Add a check that slug begins with a number for sorting
ajlende May 30, 2024
de077ad
Reorder schema keys
ajlende May 30, 2024
d9c5c85
Quick fix for redundant info in the defaultFontSizes migration docs
ajlende May 31, 2024
1dfcaa3
Update defaultSpacingSizes migration docs to include merging of spaci…
ajlende May 31, 2024
2af96a3
Update the global settings and styles guide
ajlende May 31, 2024
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
5 changes: 5 additions & 0 deletions backport-changelog/6.6/6616.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
https://github.com/WordPress/wordpress-develop/pull/6616

* https://github.com/WordPress/gutenberg/pull/58409
* https://github.com/WordPress/gutenberg/pull/61328
* https://github.com/WordPress/gutenberg/pull/61842
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ Settings related to spacing.
| padding | boolean | false | |
| units | array | px,em,rem,vh,vw,% | |
| customSpacingSize | boolean | true | |
| defaultSpacingSizes | boolean | true | |
| spacingSizes | array | | name, size, slug |
| spacingScale | object | | |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,22 @@ In theme.json v2, the default font sizes were only shown when theme sizes were n
To keep behavior similar to v2 with a v3 theme.json:
* If you do not have any `fontSizes` defined, `defaultFontSizes` can be left out or set to `true`.
* If you have some `fontSizes` defined, set `defaultFontSizes` to `false`.

#### `settings.spacing.defaultSpacingSizes`

In theme.json v2, the default spacing sizes were only shown when theme sizes were not defined. A theme providing spacing sizes with the same slugs as the defaults would always override them.

The default `spacingSizes` slugs are: `20`, `30`, `40`, `50`, `60`, `70`, and `80`. They are defined via a default `spacingScale` config with 7 steps.

The new `defaultSpacingSizes` option gives control over showing default font sizes and preventing those defaults from being overridden.

- When set to `true` it will show the default spacing sizes and prevent them from being overridden by the theme.
- When set to `false` it will hide the default spacing sizes and allow the theme to use the default slugs.

It is `true` by default when switching to v3. This is to be consistent with how other `default*` options work such as `settings.color.defaultPalette`, but differs from the behavior in v2.

In theme.json v2, the default spacing sizes were only shown when theme `spacingSizes` presets and `spacingScale` config were not defined. A theme providing font sizes with the same slugs as the defaults would always override the default ones.

To keep behavior similar to v2 with a v3 theme.json:
* If you do not have any `spacingSizes` presets or `spacingScale` config defined, `defaultSpacingSizes` can be left out or set to `true`.
* If you disabled default spacing sizes by setting `spacingScale` to `{ "steps": 0 }`, remove the `spacingScale` config and set `defaultSpacingSizes` to `false`.
187 changes: 168 additions & 19 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class WP_Theme_JSON_Gutenberg {
* @since 6.0.0 Replaced `override` with `prevent_override` and updated the
* `prevent_override` value for `color.duotone` to use `color.defaultDuotone`.
* @since 6.2.0 Added 'shadow' presets.
* @since 6.6.0 Updated the 'prevent_override' value for font size presets to use 'typography.defaultFontSizes' and spacing size presets to use `spacing.defaultSpacingSizes`.
* @since 6.6.0 Added `aspectRatios`.
* @var array
*/
Expand Down Expand Up @@ -187,7 +188,7 @@ class WP_Theme_JSON_Gutenberg {
),
array(
'path' => array( 'spacing', 'spacingSizes' ),
'prevent_override' => false,
'prevent_override' => array( 'spacing', 'defaultSpacingSizes' ),
'use_default_names' => true,
'value_key' => 'size',
'css_vars' => '--wp--preset--spacing--$slug',
Expand Down Expand Up @@ -427,13 +428,14 @@ class WP_Theme_JSON_Gutenberg {
'sticky' => null,
),
'spacing' => array(
'customSpacingSize' => null,
'spacingSizes' => null,
'spacingScale' => null,
'blockGap' => null,
'margin' => null,
'padding' => null,
'units' => null,
'customSpacingSize' => null,
'defaultSpacingSizes' => null,
'spacingSizes' => null,
'spacingScale' => null,
'blockGap' => null,
'margin' => null,
'padding' => null,
'units' => null,
),
'shadow' => array(
'presets' => null,
Expand Down Expand Up @@ -727,6 +729,8 @@ public static function get_element_class_name( $element ) {
* Constructor.
*
* @since 5.8.0
* @since 6.6.0 Key spacingScale by origin, and pre-generate the
* spacingSizes from spacingScale.
*
* @param array $theme_json A structure that follows the theme.json schema.
* @param string $origin Optional. What source of data this object represents.
Expand All @@ -742,8 +746,8 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut
$valid_block_names = array_keys( $registry->get_all_registered() );
$valid_element_names = array_keys( static::ELEMENTS );
$valid_variations = static::get_valid_block_style_variations();
$theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations );
$this->theme_json = static::maybe_opt_in_into_settings( $theme_json );
$this->theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations );
$this->theme_json = static::maybe_opt_in_into_settings( $this->theme_json );

// Internally, presets are keyed by origin.
$nodes = static::get_setting_nodes( $this->theme_json );
Expand All @@ -762,6 +766,27 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut
}
}
}

// In addition to presets, spacingScale (which generates presets) is also keyed by origin.
$scale_path = array( 'settings', 'spacing', 'spacingScale' );
$spacing_scale = _wp_array_get( $this->theme_json, $scale_path, null );
if ( null !== $spacing_scale ) {
// If the spacingScale is not already keyed by origin.
if ( empty( array_intersect( array_keys( $spacing_scale ), static::VALID_ORIGINS ) ) ) {
_wp_array_set( $this->theme_json, $scale_path, array( $origin => $spacing_scale ) );
}
}

// Pre-generate the spacingSizes from spacingScale.
$scale_path = array( 'settings', 'spacing', 'spacingScale', $origin );
$spacing_scale = _wp_array_get( $this->theme_json, $scale_path, null );
if ( isset( $spacing_scale ) ) {
$sizes_path = array( 'settings', 'spacing', 'spacingSizes', $origin );
$spacing_sizes = _wp_array_get( $this->theme_json, $sizes_path, array() );
$spacing_scale_sizes = static::compute_spacing_sizes( $spacing_scale );
$merged_spacing_sizes = static::merge_spacing_sizes( $spacing_scale_sizes, $spacing_sizes );
_wp_array_set( $this->theme_json, $sizes_path, $merged_spacing_sizes );
}
}

/**
Expand Down Expand Up @@ -2947,13 +2972,49 @@ protected static function get_metadata_boolean( $data, $path, $default_value = f
*
* @since 5.8.0
* @since 5.9.0 Duotone preset also has origins.
* @since 6.6.0 Use the spacingScale keyed by origin, and re-generate the
* spacingSizes from spacingScale.
*
* @param WP_Theme_JSON_Gutenberg $incoming Data to merge.
*/
public function merge( $incoming ) {
$incoming_data = $incoming->get_raw_data();
$this->theme_json = array_replace_recursive( $this->theme_json, $incoming_data );

/*
* Recompute all the spacing sizes based on the new hierarchy of data. In the constructor
* spacingScale and spacingSizes are both keyed by origin and VALID_ORIGINS is ordered, so
* we can allow partial spacingScale data to inherit missing data from earlier layers when
* computing the spacing sizes.
*
* This happens before the presets are merged to ensure that default spacing sizes can be
* removed from the theme origin if $prevent_override is true.
*/
$flattened_spacing_scale = array();
foreach ( static::VALID_ORIGINS as $origin ) {
$scale_path = array( 'settings', 'spacing', 'spacingScale', $origin );

// Apply the base spacing scale to the current layer.
$base_spacing_scale = _wp_array_get( $this->theme_json, $scale_path, array() );
$flattened_spacing_scale = array_replace( $flattened_spacing_scale, $base_spacing_scale );

$spacing_scale = _wp_array_get( $incoming_data, $scale_path, null );
if ( ! isset( $spacing_scale ) ) {
continue;
}

// Allow partial scale settings by merging with lower layers.
$flattened_spacing_scale = array_replace( $flattened_spacing_scale, $spacing_scale );

// Generate and merge the scales for this layer.
$sizes_path = array( 'settings', 'spacing', 'spacingSizes', $origin );
$spacing_sizes = _wp_array_get( $incoming_data, $sizes_path, array() );
$spacing_scale_sizes = static::compute_spacing_sizes( $flattened_spacing_scale );
$merged_spacing_sizes = static::merge_spacing_sizes( $spacing_scale_sizes, $spacing_sizes );

_wp_array_set( $incoming_data, $sizes_path, $merged_spacing_sizes );
}

/*
* The array_replace_recursive algorithm merges at the leaf level,
* but we don't want leaf arrays to be merged, so we overwrite it.
Expand Down Expand Up @@ -3733,12 +3794,19 @@ public function get_data() {
/**
* Sets the spacingSizes array based on the spacingScale values from theme.json.
*
* No longer used since theme.json version 3 as the spacingSizes are now
* automatically generated during construction and merge instead of manually
* set in the resolver.
*
* @since 6.1.0
* @deprecated 6.6.0
*
* @return null|void
*/
public function set_spacing_sizes() {
$spacing_scale = $this->theme_json['settings']['spacing']['spacingScale'] ?? array();
_deprecated_function( __METHOD__, '6.6.0' );

$spacing_scale = $this->theme_json['settings']['spacing']['spacingScale']['default'] ?? array();

// Gutenberg didn't have the 1st isset check.
if ( ! isset( $spacing_scale['steps'] )
Expand All @@ -3762,6 +3830,94 @@ public function set_spacing_sizes() {
return null;
}

$spacing_sizes = static::compute_spacing_sizes( $spacing_scale );

// If there are 7 or less steps in the scale revert to numbers for labels instead of t-shirt sizes.
if ( $spacing_scale['steps'] <= 7 ) {
for ( $spacing_sizes_count = 0; $spacing_sizes_count < count( $spacing_sizes ); $spacing_sizes_count++ ) {
$spacing_sizes[ $spacing_sizes_count ]['name'] = (string) ( $spacing_sizes_count + 1 );
}
}

_wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes );
}

/**
* Merges two sets of spacing size presets.
*
* @since 6.6.0
*
* @param array $base The base set of spacing sizes.
* @param array $incoming The set of spacing sizes to merge with the base. Duplicate slugs will override the base values.
* @return array The merged set of spacing sizes.
*/
private static function merge_spacing_sizes( $base, $incoming ) {
$merged = array();
foreach ( $base as $item ) {
$merged[ $item['slug'] ] = $item;
}
foreach ( $incoming as $item ) {
$merged[ $item['slug'] ] = $item;
}
return array_values( $merged );
}

/**
* Generates a set of spacing sizes by starting with a medium size and
* applying an operator with an increment value to generate the rest of the
* sizes outward from the medium size. The medium slug is '50' with the rest
* of the slugs being 10 apart. The generated names use t-shirt sizing.
*
* Example:
*
* $spacing_scale = array(
* 'steps' => 4,
* 'mediumStep' => 16,
* 'unit' => 'px',
* 'operator' => '+',
* 'increment' => 2,
* );
* $spacing_sizes = static::compute_spacing_sizes( $spacing_scale );
* // -> array(
* // array( 'name' => 'Small', 'slug' => '40', 'size' => '14px' ),
* // array( 'name' => 'Medium', 'slug' => '50', 'size' => '16px' ),
* // array( 'name' => 'Large', 'slug' => '60', 'size' => '18px' ),
* // array( 'name' => 'X-Large', 'slug' => '70', 'size' => '20px' ),
* // )
*
* @since 6.6.0
*
* @param array $spacing_scale {
* The spacing scale values. All are required.
*
* @type int $steps The number of steps in the scale. (up to 10 steps are supported.)
* @type float $mediumStep The middle value that gets the slug '50'. (For even number of steps, this becomes the first middle value.)
* @type string $unit The CSS unit to use for the sizes.
* @type string $operator The mathematical operator to apply to generate the other sizes. Either '+' or '*'.
* @type float $increment The value used with the operator to generate the other sizes.
* }
* @return array The spacing sizes presets or an empty array if some spacing scale values are missing or invalid.
*/
private static function compute_spacing_sizes( $spacing_scale ) {
/*
* This condition is intentionally missing some checks on ranges for the values in order to
* keep backwards compatibility with the previous implementation.
*/
if (
! isset( $spacing_scale['steps'] ) ||
! is_numeric( $spacing_scale['steps'] ) ||
0 === $spacing_scale['steps'] ||
! isset( $spacing_scale['mediumStep'] ) ||
! is_numeric( $spacing_scale['mediumStep'] ) ||
! isset( $spacing_scale['unit'] ) ||
! isset( $spacing_scale['operator'] ) ||
( '+' !== $spacing_scale['operator'] && '*' !== $spacing_scale['operator'] ) ||
! isset( $spacing_scale['increment'] ) ||
! is_numeric( $spacing_scale['increment'] )
Comment on lines +3907 to +3916
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker but I wonder if we should have unit tests for this mammoth of a condition? Can be a follow up

Copy link
Contributor Author

@ajlende ajlende May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave it for a follow-up. There are already some tests for the existence of these conditions, but not the things it doesn't catch like "steps": 20 only generating 15 steps.

) {
return array();
}

$unit = '%' === $spacing_scale['unit'] ? '%' : sanitize_title( $spacing_scale['unit'] );
$current_step = $spacing_scale['mediumStep'];
$steps_mid_point = round( $spacing_scale['steps'] / 2, 0 );
Expand Down Expand Up @@ -3844,14 +4000,7 @@ public function set_spacing_sizes() {
$spacing_sizes[] = $above_sizes_item;
}

// If there are 7 or less steps in the scale revert to numbers for labels instead of t-shirt sizes.
if ( $spacing_scale['steps'] <= 7 ) {
for ( $spacing_sizes_count = 0; $spacing_sizes_count < count( $spacing_sizes ); $spacing_sizes_count++ ) {
$spacing_sizes[ $spacing_sizes_count ]['name'] = (string) ( $spacing_sizes_count + 1 );
}
}

_wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes );
return $spacing_sizes;
}

/**
Expand Down
3 changes: 0 additions & 3 deletions lib/class-wp-theme-json-resolver-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,6 @@ public static function get_merged_data( $origin = 'custom' ) {
$result = new WP_Theme_JSON_Gutenberg();
$result->merge( static::get_core_data() );
if ( 'default' === $origin ) {
$result->set_spacing_sizes();
Copy link
Contributor Author

@ajlende ajlende May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to WP_Theme_JSON_Gutenberg->__construct() so that the generated presets for each origin are merged properly rather than always being set as default origin.

return $result;
}

Expand All @@ -611,12 +610,10 @@ public static function get_merged_data( $origin = 'custom' ) {

$result->merge( static::get_theme_data() );
if ( 'theme' === $origin ) {
$result->set_spacing_sizes();
return $result;
}

$result->merge( static::get_user_data() );
$result->set_spacing_sizes();
return $result;
}

Expand Down
34 changes: 33 additions & 1 deletion lib/class-wp-theme-json-schema-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ private static function migrate_v2_to_v3( $old ) {
* affect the generated CSS. And in v2 we provided default font sizes
* when the theme did not provide any.
*/
if ( isset( $new['settings']['typography']['fontSizes'] ) ) {
if ( isset( $old['settings']['typography']['fontSizes'] ) ) {
if ( ! isset( $new['settings'] ) ) {
$new['settings'] = array();
}
Expand All @@ -141,6 +141,38 @@ private static function migrate_v2_to_v3( $old ) {
$new['settings']['typography']['defaultFontSizes'] = false;
}

/*
* Similarly to defaultFontSizes, we need to migrate defaultSpacingSizes
* as it controls the PRESETS_METADATA prevent_override which was
* previously hardcoded to false. This only needs to happen when the
* theme provided spacing sizes via spacingSizes or spacingScale.
*/
if (
isset( $old['settings']['spacing']['spacingSizes'] ) ||
isset( $old['settings']['spacing']['spacingScale'] )
) {
if ( ! isset( $new['settings'] ) ) {
$new['settings'] = array();
}
if ( ! isset( $new['settings']['spacing'] ) ) {
$new['settings']['spacing'] = array();
}
$new['settings']['spacing']['defaultSpacingSizes'] = false;
}

/*
* In v3 spacingSizes is merged with the generated spacingScale sizes
* instead of completely replacing them. The v3 behavior is what was
* documented for the v2 schema, but the code never actually did work
* that way. Instead of surprising users with a behavior change two
* years after the fact at the same time as a v3 update is introduced,
* we'll continue using the "bugged" behavior for v2 themes. And treat
* the "bug fix" as a breaking change for v3.
*/
if ( isset( $old['settings']['spacing']['spacingSizes'] ) ) {
unset( $new['settings']['spacing']['spacingScale'] );
}

return $new;
}

Expand Down
1 change: 1 addition & 0 deletions lib/theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@
"margin": false,
"padding": false,
"customSpacingSize": true,
"defaultSpacingSizes": true,
"units": [ "px", "em", "rem", "vh", "vw", "%" ],
"spacingScale": {
"operator": "*",
Expand Down
Loading