@wordpress/style-engine 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +162 -4
  3. package/class-wp-style-engine-css-declarations.php +27 -3
  4. package/class-wp-style-engine-css-rule.php +17 -3
  5. package/class-wp-style-engine-css-rules-store.php +49 -0
  6. package/class-wp-style-engine-processor.php +68 -26
  7. package/class-wp-style-engine.php +236 -107
  8. package/package.json +2 -2
  9. package/phpunit/class-wp-style-engine-css-declarations-test.php +85 -7
  10. package/phpunit/class-wp-style-engine-css-rule-test.php +38 -7
  11. package/phpunit/class-wp-style-engine-css-rules-store-test.php +44 -2
  12. package/phpunit/class-wp-style-engine-processor-test.php +170 -31
  13. package/phpunit/class-wp-style-engine-test.php +219 -22
  14. package/build-types/index.d.ts +0 -23
  15. package/build-types/index.d.ts.map +0 -1
  16. package/build-types/styles/border/index.d.ts +0 -10
  17. package/build-types/styles/border/index.d.ts.map +0 -1
  18. package/build-types/styles/color/background.d.ts +0 -14
  19. package/build-types/styles/color/background.d.ts.map +0 -1
  20. package/build-types/styles/color/gradient.d.ts +0 -14
  21. package/build-types/styles/color/gradient.d.ts.map +0 -1
  22. package/build-types/styles/color/index.d.ts +0 -10
  23. package/build-types/styles/color/index.d.ts.map +0 -1
  24. package/build-types/styles/color/text.d.ts +0 -14
  25. package/build-types/styles/color/text.d.ts.map +0 -1
  26. package/build-types/styles/constants.d.ts +0 -4
  27. package/build-types/styles/constants.d.ts.map +0 -1
  28. package/build-types/styles/index.d.ts +0 -5
  29. package/build-types/styles/index.d.ts.map +0 -1
  30. package/build-types/styles/spacing/index.d.ts +0 -6
  31. package/build-types/styles/spacing/index.d.ts.map +0 -1
  32. package/build-types/styles/spacing/margin.d.ts +0 -10
  33. package/build-types/styles/spacing/margin.d.ts.map +0 -1
  34. package/build-types/styles/spacing/padding.d.ts +0 -10
  35. package/build-types/styles/spacing/padding.d.ts.map +0 -1
  36. package/build-types/styles/typography/index.d.ts +0 -14
  37. package/build-types/styles/typography/index.d.ts.map +0 -1
  38. package/build-types/styles/utils.d.ts +0 -48
  39. package/build-types/styles/utils.d.ts.map +0 -1
  40. package/build-types/types.d.ts +0 -86
  41. package/build-types/types.d.ts.map +0 -1
  42. package/tsconfig.tsbuildinfo +0 -1
@@ -23,13 +23,6 @@ if ( class_exists( 'WP_Style_Engine' ) ) {
23
23
  * @access private
24
24
  */
25
25
  class WP_Style_Engine {
26
- /**
27
- * Container for the main instance of the class.
28
- *
29
- * @var WP_Style_Engine|null
30
- */
31
- private static $instance = null;
32
-
33
26
  /**
34
27
  * Style definitions that contain the instructions to
35
28
  * parse/output valid Gutenberg styles from a block's attributes.
@@ -222,22 +215,7 @@ class WP_Style_Engine {
222
215
  );
223
216
 
224
217
  /**
225
- * Utility method to retrieve the main instance of the class.
226
- *
227
- * The instance will be created if it does not exist yet.
228
- *
229
- * @return WP_Style_Engine The main instance.
230
- */
231
- public static function get_instance() {
232
- if ( null === static::$instance ) {
233
- static::$instance = new static();
234
- }
235
-
236
- return static::$instance;
237
- }
238
-
239
- /**
240
- * Extracts the slug in kebab case from a preset string, e.g., "heavenly-blue" from 'var:preset|color|heavenlyBlue'.
218
+ * Util: Extracts the slug in kebab case from a preset string, e.g., "heavenly-blue" from 'var:preset|color|heavenlyBlue'.
241
219
  *
242
220
  * @param string? $style_value A single css preset value.
243
221
  * @param string $property_key The CSS property that is the second element of the preset string. Used for matching.
@@ -253,7 +231,7 @@ class WP_Style_Engine {
253
231
  }
254
232
 
255
233
  /**
256
- * Generates a css var string, eg var(--wp--preset--color--background) from a preset string, eg. `var:preset|space|50`.
234
+ * Util: Generates a css var string, eg var(--wp--preset--color--background) from a preset string, eg. `var:preset|space|50`.
257
235
  *
258
236
  * @param string $style_value A single css preset value.
259
237
  * @param array $css_vars The css var patterns used to generate the var string.
@@ -275,7 +253,7 @@ class WP_Style_Engine {
275
253
  }
276
254
 
277
255
  /**
278
- * Checks whether an incoming block style value is valid.
256
+ * Util: Checks whether an incoming block style value is valid.
279
257
  *
280
258
  * @param string? $style_value A single css preset value.
281
259
  *
@@ -293,6 +271,80 @@ class WP_Style_Engine {
293
271
  return true;
294
272
  }
295
273
 
274
+ /**
275
+ * Stores a CSS rule using the provide CSS selector and CSS declarations.
276
+ *
277
+ * @param string $store_key A valid store key.
278
+ * @param string $css_selector When a selector is passed, the function will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values.
279
+ * @param array $css_declarations An array of parsed CSS property => CSS value pairs.
280
+ *
281
+ * @return void.
282
+ */
283
+ public static function store_css_rule( $store_key, $css_selector, $css_declarations ) {
284
+ if ( empty( $store_key ) || empty( $css_selector ) || empty( $css_declarations ) ) {
285
+ return;
286
+ }
287
+ static::get_store( $store_key )->add_rule( $css_selector )->add_declarations( $css_declarations );
288
+ }
289
+
290
+ /**
291
+ * Returns a store by store key.
292
+ *
293
+ * @param string $store_key A store key.
294
+ *
295
+ * @return WP_Style_Engine_CSS_Rules_Store
296
+ */
297
+ public static function get_store( $store_key ) {
298
+ return WP_Style_Engine_CSS_Rules_Store::get_store( $store_key );
299
+ }
300
+
301
+ /**
302
+ * Returns classnames and CSS based on the values in a styles object.
303
+ * Return values are parsed based on the instructions in BLOCK_STYLE_DEFINITIONS_METADATA.
304
+ *
305
+ * @param array $block_styles The style object.
306
+ * @param array $options array(
307
+ * 'selector' => (string) When a selector is passed, `generate()` will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values.
308
+ * 'convert_vars_to_classnames' => (boolean) Whether to skip converting CSS var:? values to var( --wp--preset--* ) values. Default is `false`.
309
+ * );.
310
+ *
311
+ * @return array array(
312
+ * 'declarations' => (array) An array of parsed CSS property => CSS value pairs.
313
+ * 'classnames' => (array) A flat array of classnames.
314
+ * );
315
+ */
316
+ public static function parse_block_styles( $block_styles, $options ) {
317
+ if ( empty( $block_styles ) || ! is_array( $block_styles ) ) {
318
+ return array();
319
+ }
320
+
321
+ $css_declarations = array();
322
+ $classnames = array();
323
+ $should_skip_css_vars = isset( $options['convert_vars_to_classnames'] ) && true === $options['convert_vars_to_classnames'];
324
+
325
+ // Collect CSS and classnames.
326
+ foreach ( static::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group_key => $definition_group_style ) {
327
+ if ( empty( $block_styles[ $definition_group_key ] ) ) {
328
+ continue;
329
+ }
330
+ foreach ( $definition_group_style as $style_definition ) {
331
+ $style_value = _wp_array_get( $block_styles, $style_definition['path'], null );
332
+
333
+ if ( ! static::is_valid_style_value( $style_value ) ) {
334
+ continue;
335
+ }
336
+
337
+ $classnames = array_merge( $classnames, static::get_classnames( $style_value, $style_definition ) );
338
+ $css_declarations = array_merge( $css_declarations, static::get_css_declarations( $style_value, $style_definition, $should_skip_css_vars ) );
339
+ }
340
+ }
341
+
342
+ return array(
343
+ 'classnames' => $classnames,
344
+ 'declarations' => $css_declarations,
345
+ );
346
+ }
347
+
296
348
  /**
297
349
  * Returns classnames, and generates classname(s) from a CSS preset property pattern, e.g., 'var:preset|color|heavenly-blue'.
298
350
  *
@@ -339,10 +391,7 @@ class WP_Style_Engine {
339
391
  * @return array An array of CSS definitions, e.g., array( "$property" => "$value" ).
340
392
  */
341
393
  protected static function get_css_declarations( $style_value, $style_definition, $should_skip_css_vars = false ) {
342
- if (
343
- isset( $style_definition['value_func'] ) &&
344
- is_callable( $style_definition['value_func'] )
345
- ) {
394
+ if ( isset( $style_definition['value_func'] ) && is_callable( $style_definition['value_func'] ) ) {
346
395
  return call_user_func( $style_definition['value_func'], $style_value, $style_definition, $should_skip_css_vars );
347
396
  }
348
397
 
@@ -369,7 +418,9 @@ class WP_Style_Engine {
369
418
  if ( is_string( $value ) && strpos( $value, 'var:' ) !== false && ! $should_skip_css_vars && ! empty( $style_definition['css_vars'] ) ) {
370
419
  $value = static::get_css_var_value( $value, $style_definition['css_vars'] );
371
420
  }
421
+
372
422
  $individual_property = sprintf( $style_property_keys['individual'], _wp_to_kebab_case( $key ) );
423
+
373
424
  if ( $individual_property && static::is_valid_style_value( $value ) ) {
374
425
  $css_declarations[ $individual_property ] = $value;
375
426
  }
@@ -381,73 +432,6 @@ class WP_Style_Engine {
381
432
  return $css_declarations;
382
433
  }
383
434
 
384
- /**
385
- * Returns classnames and CSS based on the values in a block attributes.styles object.
386
- * Return values are parsed based on the instructions in BLOCK_STYLE_DEFINITIONS_METADATA.
387
- *
388
- * @param array $block_styles Styles from a block's attributes object.
389
- * @param array $options array(
390
- * 'selector' => (string) When a selector is passed, `generate()` will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values.
391
- * 'convert_vars_to_classnames' => (boolean) Whether to skip converting CSS var:? values to var( --wp--preset--* ) values. Default is `false`.
392
- * );.
393
- *
394
- * @return array|null array(
395
- * 'css' => (string) A CSS ruleset formatted to be placed in an HTML `style` attribute or tag. Default is a string of inline styles.
396
- * 'classnames' => (string) Classnames separated by a space.
397
- * );
398
- */
399
- public function get_block_supports_styles( $block_styles, $options ) {
400
- if ( empty( $block_styles ) || ! is_array( $block_styles ) ) {
401
- return null;
402
- }
403
-
404
- $css_declarations = array();
405
- $classnames = array();
406
- $should_skip_css_vars = isset( $options['convert_vars_to_classnames'] ) && true === $options['convert_vars_to_classnames'];
407
-
408
- // Collect CSS and classnames.
409
- foreach ( static::BLOCK_STYLE_DEFINITIONS_METADATA as $definition_group_key => $definition_group_style ) {
410
- if ( empty( $block_styles[ $definition_group_key ] ) ) {
411
- continue;
412
- }
413
- foreach ( $definition_group_style as $style_definition ) {
414
- $style_value = _wp_array_get( $block_styles, $style_definition['path'], null );
415
-
416
- if ( ! static::is_valid_style_value( $style_value ) ) {
417
- continue;
418
- }
419
-
420
- $classnames = array_merge( $classnames, static::get_classnames( $style_value, $style_definition ) );
421
- $css_declarations = array_merge( $css_declarations, static::get_css_declarations( $style_value, $style_definition, $should_skip_css_vars ) );
422
- }
423
- }
424
-
425
- // Build CSS rules output.
426
- $css_selector = isset( $options['selector'] ) ? $options['selector'] : null;
427
- $css_declarations = new WP_Style_Engine_CSS_Declarations( $css_declarations );
428
-
429
- // The return object.
430
- $styles_output = array();
431
- $css = $css_declarations->get_declarations_string();
432
-
433
- // Return css, if any.
434
- if ( ! empty( $css ) ) {
435
- $styles_output['css'] = $css;
436
- $styles_output['declarations'] = $css_declarations->get_declarations();
437
- // Return an entire rule if there is a selector.
438
- if ( $css_selector ) {
439
- $styles_output['css'] = $css_selector . ' { ' . $css . ' }';
440
- }
441
- }
442
-
443
- // Return classnames, if any.
444
- if ( ! empty( $classnames ) ) {
445
- $styles_output['classnames'] = implode( ' ', array_unique( $classnames ) );
446
- }
447
-
448
- return $styles_output;
449
- }
450
-
451
435
  /**
452
436
  * Style value parser that returns a CSS definition array comprising style properties
453
437
  * that have keys representing individual style properties, otherwise known as longhand CSS properties.
@@ -494,32 +478,177 @@ class WP_Style_Engine {
494
478
  }
495
479
  return $css_declarations;
496
480
  }
481
+
482
+ /**
483
+ * Returns compiled CSS from css_declarations.
484
+ *
485
+ * @param array $css_declarations An array of parsed CSS property => CSS value pairs.
486
+ * @param string $css_selector When a selector is passed, the function will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values.
487
+ *
488
+ * @return string A compiled CSS string.
489
+ */
490
+ public static function compile_css( $css_declarations, $css_selector ) {
491
+ if ( empty( $css_declarations ) || ! is_array( $css_declarations ) ) {
492
+ return '';
493
+ }
494
+
495
+ // Return an entire rule if there is a selector.
496
+ if ( $css_selector ) {
497
+ $css_rule = new WP_Style_Engine_CSS_Rule( $css_selector, $css_declarations );
498
+ return $css_rule->get_css();
499
+ }
500
+ $css_declarations = new WP_Style_Engine_CSS_Declarations( $css_declarations );
501
+
502
+ return $css_declarations->get_declarations_string();
503
+ }
504
+
505
+ /**
506
+ * Returns a compiled stylesheet from stored CSS rules.
507
+ *
508
+ * @param WP_Style_Engine_CSS_Rule[] $css_rules An array of WP_Style_Engine_CSS_Rule objects from a store or otherwise.
509
+ *
510
+ * @return string A compiled stylesheet from stored CSS rules.
511
+ */
512
+ public static function compile_stylesheet_from_css_rules( $css_rules ) {
513
+ $processor = new WP_Style_Engine_Processor();
514
+ $processor->add_rules( $css_rules );
515
+ return $processor->get_css( array( 'prettify' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) );
516
+ }
497
517
  }
498
518
 
499
519
  /**
500
- * Global public interface method to WP_Style_Engine->get_block_supports_styles to generate block styles from a single block style object.
501
- * See: https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/
520
+ * Global public interface method to generate styles from a single style object, e.g.,
521
+ * the value of a block's attributes.style object or the top level styles in theme.json.
522
+ * See: https://developer.wordpress.org/block-editor/reference-guides/theme-json-reference/theme-json-living/#styles and
523
+ * https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/
502
524
  *
503
525
  * Example usage:
504
526
  *
505
- * $styles = wp_style_engine_get_block_supports_styles( array( 'color' => array( 'text' => '#cccccc' ) ) );
527
+ * $styles = wp_style_engine_get_styles( array( 'color' => array( 'text' => '#cccccc' ) ) );
506
528
  * // Returns `array( 'css' => 'color: #cccccc', 'declarations' => array( 'color' => '#cccccc' ), 'classnames' => 'has-color' )`.
507
529
  *
508
530
  * @access public
509
531
  *
510
- * @param array $block_styles The value of a block's attributes.style.
511
- * @param array<string> $options An array of options to determine the output.
532
+ * @param array $block_styles The style object.
533
+ * @param array<string|boolean> $options array(
534
+ * 'context' => (string) An identifier describing the origin of the style object, e.g., 'block-supports' or 'global-styles'. Default is 'block-supports'.
535
+ * 'enqueue' => (boolean) When `true` will attempt to store and enqueue for rendering on the frontend.
536
+ * 'convert_vars_to_classnames' => (boolean) Whether to skip converting CSS var:? values to var( --wp--preset--* ) values. Default is `false`.
537
+ * 'selector' => (string) When a selector is passed, `generate()` will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values.
538
+ * );.
512
539
  *
513
- * @return array<string>|null array(
540
+ * @return array<string|array>|null array(
514
541
  * 'css' => (string) A CSS ruleset or declarations block formatted to be placed in an HTML `style` attribute or tag.
515
542
  * 'declarations' => (array) An array of property/value pairs representing parsed CSS declarations.
516
543
  * 'classnames' => (string) Classnames separated by a space.
517
544
  * );
518
545
  */
519
- function wp_style_engine_get_block_supports_styles( $block_styles, $options = array() ) {
520
- if ( class_exists( 'WP_Style_Engine' ) ) {
521
- $style_engine = WP_Style_Engine::get_instance();
522
- return $style_engine->get_block_supports_styles( $block_styles, $options );
546
+ function wp_style_engine_get_styles( $block_styles, $options = array() ) {
547
+ if ( ! class_exists( 'WP_Style_Engine' ) ) {
548
+ return array();
549
+ }
550
+ $defaults = array(
551
+ 'selector' => null,
552
+ 'context' => 'block-supports',
553
+ 'convert_vars_to_classnames' => false,
554
+ 'enqueue' => false,
555
+ );
556
+
557
+ $options = wp_parse_args( $options, $defaults );
558
+ $parsed_styles = null;
559
+
560
+ // Block supports styles.
561
+ if ( 'block-supports' === $options['context'] ) {
562
+ $parsed_styles = WP_Style_Engine::parse_block_styles( $block_styles, $options );
563
+ }
564
+
565
+ // Output.
566
+ $styles_output = array();
567
+
568
+ if ( ! $parsed_styles ) {
569
+ return $styles_output;
570
+ }
571
+
572
+ if ( ! empty( $parsed_styles['declarations'] ) ) {
573
+ $styles_output['css'] = WP_Style_Engine::compile_css( $parsed_styles['declarations'], $options['selector'] );
574
+ $styles_output['declarations'] = $parsed_styles['declarations'];
575
+ if ( true === $options['enqueue'] ) {
576
+ WP_Style_Engine::store_css_rule( $options['context'], $options['selector'], $parsed_styles['declarations'] );
577
+ }
578
+ }
579
+
580
+ if ( ! empty( $parsed_styles['classnames'] ) ) {
581
+ $styles_output['classnames'] = implode( ' ', array_unique( $parsed_styles['classnames'] ) );
582
+ }
583
+
584
+ return array_filter( $styles_output );
585
+ }
586
+
587
+ /**
588
+ * Returns compiled CSS from a collection of selectors and declarations.
589
+ * This won't add to any store, but is useful for returning a compiled style sheet from any CSS selector + declarations combos.
590
+ *
591
+ * @access public
592
+ *
593
+ * @param array<array> $css_rules array(
594
+ * array(
595
+ * 'selector' => (string) A CSS selector.
596
+ * declarations' => (boolean) An array of CSS definitions, e.g., array( "$property" => "$value" ).
597
+ * )
598
+ * );.
599
+ * @param array<string> $options array(
600
+ * 'context' => (string) An identifier describing the origin of the style object, e.g., 'block-supports' or 'global-styles'. Default is 'block-supports'.
601
+ * 'enqueue' => (boolean) When `true` will attempt to store and enqueue for rendering on the frontend.
602
+ * );.
603
+ *
604
+ * @return string A compiled CSS string.
605
+ */
606
+ function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = array() ) {
607
+ if ( ! class_exists( 'WP_Style_Engine' ) || empty( $css_rules ) ) {
608
+ return '';
609
+ }
610
+
611
+ $defaults = array(
612
+ 'context' => 'block-supports',
613
+ 'enqueue' => false,
614
+ );
615
+ $options = wp_parse_args( $options, $defaults );
616
+ $css_rule_objects = array();
617
+
618
+ foreach ( $css_rules as $css_rule ) {
619
+ if ( empty( $css_rule['selector'] ) || empty( $css_rule['declarations'] ) || ! is_array( $css_rule['declarations'] ) ) {
620
+ continue;
621
+ }
622
+
623
+ if ( true === $options['enqueue'] ) {
624
+ WP_Style_Engine::store_css_rule( $options['context'], $css_rule['selector'], $css_rule['declarations'] );
625
+ }
626
+
627
+ $css_rule_objects[] = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] );
628
+ }
629
+
630
+ if ( empty( $css_rule_objects ) ) {
631
+ return '';
632
+ }
633
+
634
+ return WP_Style_Engine::compile_stylesheet_from_css_rules( $css_rule_objects );
635
+ }
636
+
637
+ /**
638
+ * Returns compiled CSS from a store, if found.
639
+ *
640
+ * @access public
641
+ *
642
+ * @param string $store_key A valid store key.
643
+ *
644
+ * @return string A compiled CSS string.
645
+ */
646
+ function wp_style_engine_get_stylesheet_from_store( $store_key ) {
647
+ if ( ! class_exists( 'WP_Style_Engine' ) || empty( $store_key ) ) {
648
+ return '';
523
649
  }
524
- return null;
650
+
651
+ $store = WP_Style_Engine::get_store( $store_key );
652
+
653
+ return WP_Style_Engine::compile_stylesheet_from_css_rules( $store->get_all_rules() );
525
654
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/style-engine",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "WordPress Style engine.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -34,5 +34,5 @@
34
34
  "publishConfig": {
35
35
  "access": "public"
36
36
  },
37
- "gitHead": "0315dbc240cb2aa146d7c1bafd251f004b88300e"
37
+ "gitHead": "08358f53b627a15148c3a3e433cdf58cf8714aa4"
38
38
  }
@@ -84,24 +84,102 @@ class WP_Style_Engine_CSS_Declarations_Test extends WP_UnitTestCase {
84
84
  $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
85
85
 
86
86
  $this->assertSame(
87
- 'color: red; border-top-left-radius: 99px; text-decoration: underline;',
87
+ 'color:red;border-top-left-radius:99px;text-decoration:underline;',
88
88
  $css_declarations->get_declarations_string()
89
89
  );
90
90
  }
91
91
 
92
+ /**
93
+ * Should compile css declarations into a prettified css declarations block string.
94
+ */
95
+ public function test_generate_prettified_css_declarations_string() {
96
+ $input_declarations = array(
97
+ 'color' => 'red',
98
+ 'border-top-left-radius' => '99px',
99
+ 'text-decoration' => 'underline',
100
+ );
101
+ $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
102
+
103
+ $this->assertSame(
104
+ 'color: red; border-top-left-radius: 99px; text-decoration: underline;',
105
+ $css_declarations->get_declarations_string( true )
106
+ );
107
+ }
108
+
109
+ /**
110
+ * Should compile css declarations into a prettified and indented css declarations block string.
111
+ */
112
+ public function test_generate_prettified_with_indent_css_declarations_string() {
113
+ $input_declarations = array(
114
+ 'color' => 'red',
115
+ 'border-top-left-radius' => '99px',
116
+ 'text-decoration' => 'underline',
117
+ );
118
+ $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
119
+
120
+ $this->assertSame(
121
+ ' color: red;
122
+ border-top-left-radius: 99px;
123
+ text-decoration: underline;',
124
+ $css_declarations->get_declarations_string( true, 1 )
125
+ );
126
+ }
127
+
128
+ /**
129
+ * Should compile css declarations into a css declarations block string.
130
+ */
131
+ public function test_generate_prettified_with_more_indents_css_declarations_string() {
132
+ $input_declarations = array(
133
+ 'color' => 'red',
134
+ 'border-top-left-radius' => '99px',
135
+ 'text-decoration' => 'underline',
136
+ );
137
+ $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
138
+
139
+ $this->assertSame(
140
+ ' color: red;
141
+ border-top-left-radius: 99px;
142
+ text-decoration: underline;',
143
+ $css_declarations->get_declarations_string( true, 2 )
144
+ );
145
+ }
146
+
92
147
  /**
93
148
  * Should escape values and run the CSS through safecss_filter_attr.
94
149
  */
95
150
  public function test_remove_unsafe_properties_and_values() {
96
151
  $input_declarations = array(
97
- 'color' => '<red/>',
152
+ 'color' => 'url("https://wordpress.org")',
98
153
  'margin-right' => '10em',
99
154
  'potato' => 'uppercase',
100
155
  );
101
156
  $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
102
157
 
103
158
  $this->assertSame(
104
- 'color: &lt;red/&gt;; margin-right: 10em;',
159
+ 'margin-right:10em;',
160
+ $css_declarations->get_declarations_string()
161
+ );
162
+ }
163
+
164
+ /**
165
+ * Should allow calc, clamp, min, max, and minmax CSS functions.
166
+ */
167
+ public function test_allow_particular_css_functions() {
168
+ $input_declarations = array(
169
+ 'background' => 'var(--wp--preset--color--primary, 10px)', // Simple var().
170
+ 'font-size' => 'clamp(36.00rem, calc(32.00rem + 10.00vw), 40.00rem)', // Nested clamp().
171
+ 'width' => 'min(150vw, 100px)',
172
+ 'min-width' => 'max(150vw, 100px)',
173
+ 'max-width' => 'minmax(400px, 50%)',
174
+ 'padding' => 'calc(80px * -1)',
175
+ 'background-image' => 'url("https://wordpress.org")',
176
+ 'line-height' => 'url("https://wordpress.org")',
177
+ 'margin' => 'illegalfunction(30px)',
178
+ );
179
+ $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
180
+
181
+ $this->assertSame(
182
+ 'background:var(--wp--preset--color--primary, 10px);font-size:clamp(36.00rem, calc(32.00rem + 10.00vw), 40.00rem);width:min(150vw, 100px);min-width:max(150vw, 100px);max-width:minmax(400px, 50%);padding:calc(80px * -1);background-image:url("https://wordpress.org");',
105
183
  $css_declarations->get_declarations_string()
106
184
  );
107
185
  }
@@ -118,13 +196,13 @@ class WP_Style_Engine_CSS_Declarations_Test extends WP_UnitTestCase {
118
196
  $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
119
197
 
120
198
  $this->assertSame(
121
- 'color: tomato; margin: 10em 10em 20em 1px; font-family: Happy Font serif;',
199
+ 'color:tomato;margin:10em 10em 20em 1px;font-family:Happy Font serif;',
122
200
  $css_declarations->get_declarations_string()
123
201
  );
124
202
 
125
203
  $css_declarations->remove_declaration( 'color' );
126
204
  $this->assertSame(
127
- 'margin: 10em 10em 20em 1px; font-family: Happy Font serif;',
205
+ 'margin:10em 10em 20em 1px;font-family:Happy Font serif;',
128
206
  $css_declarations->get_declarations_string()
129
207
  );
130
208
  }
@@ -141,13 +219,13 @@ class WP_Style_Engine_CSS_Declarations_Test extends WP_UnitTestCase {
141
219
  $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
142
220
 
143
221
  $this->assertSame(
144
- 'color: cucumber; margin: 10em 10em 20em 1px; font-family: Happy Font serif;',
222
+ 'color:cucumber;margin:10em 10em 20em 1px;font-family:Happy Font serif;',
145
223
  $css_declarations->get_declarations_string()
146
224
  );
147
225
 
148
226
  $css_declarations->remove_declarations( array( 'color', 'margin' ) );
149
227
  $this->assertSame(
150
- 'font-family: Happy Font serif;',
228
+ 'font-family:Happy Font serif;',
151
229
  $css_declarations->get_declarations_string()
152
230
  );
153
231
  }
@@ -27,7 +27,7 @@ class WP_Style_Engine_CSS_Rule_Test extends WP_UnitTestCase {
27
27
 
28
28
  $this->assertSame( $selector, $css_rule->get_selector() );
29
29
 
30
- $expected = "$selector {{$css_declarations->get_declarations_string()}}";
30
+ $expected = "$selector{{$css_declarations->get_declarations_string()}}";
31
31
  $this->assertSame( $expected, $css_rule->get_css() );
32
32
  }
33
33
 
@@ -45,12 +45,12 @@ class WP_Style_Engine_CSS_Rule_Test extends WP_UnitTestCase {
45
45
  $css_rule = new WP_Style_Engine_CSS_Rule( $selector, $first_declaration );
46
46
  $css_rule->add_declarations( new WP_Style_Engine_CSS_Declarations( $overwrite_first_declaration ) );
47
47
 
48
- $expected = '.taggart {font-size: 4px;}';
48
+ $expected = '.taggart{font-size:4px;}';
49
49
  $this->assertSame( $expected, $css_rule->get_css() );
50
50
  }
51
51
 
52
52
  /**
53
- * Should set selector and rules on instantiation.
53
+ * Should add declarations.
54
54
  */
55
55
  public function test_add_declarations() {
56
56
  // Declarations using a WP_Style_Engine_CSS_Declarations object.
@@ -60,12 +60,12 @@ class WP_Style_Engine_CSS_Rule_Test extends WP_UnitTestCase {
60
60
  $css_rule = new WP_Style_Engine_CSS_Rule( '.hill-street-blues', $some_css_declarations );
61
61
  $css_rule->add_declarations( $some_more_css_declarations );
62
62
 
63
- $expected = '.hill-street-blues {margin-top: 10px; font-size: 1rem;}';
63
+ $expected = '.hill-street-blues{margin-top:10px;font-size:1rem;}';
64
64
  $this->assertSame( $expected, $css_rule->get_css() );
65
65
  }
66
66
 
67
67
  /**
68
- * Should set selector and rules on instantiation.
68
+ * Should set selector.
69
69
  */
70
70
  public function test_set_selector() {
71
71
  $selector = '.taggart';
@@ -79,7 +79,7 @@ class WP_Style_Engine_CSS_Rule_Test extends WP_UnitTestCase {
79
79
  }
80
80
 
81
81
  /**
82
- * Should set selector and rules on instantiation.
82
+ * Should generate CSS rules.
83
83
  */
84
84
  public function test_get_css() {
85
85
  $selector = '.chips';
@@ -89,8 +89,39 @@ class WP_Style_Engine_CSS_Rule_Test extends WP_UnitTestCase {
89
89
  );
90
90
  $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
91
91
  $css_rule = new WP_Style_Engine_CSS_Rule( $selector, $css_declarations );
92
- $expected = "$selector {{$css_declarations->get_declarations_string()}}";
92
+ $expected = "$selector{{$css_declarations->get_declarations_string()}}";
93
93
 
94
94
  $this->assertSame( $expected, $css_rule->get_css() );
95
95
  }
96
+
97
+ /**
98
+ * Should return empty string with no declarations.
99
+ */
100
+ public function test_get_css_no_declarations() {
101
+ $selector = '.holmes';
102
+ $input_declarations = array();
103
+ $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
104
+ $css_rule = new WP_Style_Engine_CSS_Rule( $selector, $css_declarations );
105
+
106
+ $this->assertSame( '', $css_rule->get_css() );
107
+ }
108
+
109
+ /**
110
+ * Should generate prettified CSS rules.
111
+ */
112
+ public function test_get_prettified_css() {
113
+ $selector = '.baptiste';
114
+ $input_declarations = array(
115
+ 'margin-left' => '0',
116
+ 'font-family' => 'Detective Sans',
117
+ );
118
+ $css_declarations = new WP_Style_Engine_CSS_Declarations( $input_declarations );
119
+ $css_rule = new WP_Style_Engine_CSS_Rule( $selector, $css_declarations );
120
+ $expected = '.baptiste {
121
+ margin-left: 0;
122
+ font-family: Detective Sans;
123
+ }';
124
+
125
+ $this->assertSame( $expected, $css_rule->get_css( true ) );
126
+ }
96
127
  }