Themes: Add internal-only theme.json's webfonts handler (stopgap).

Adds `_wp_theme_json_webfonts_handler()` for handling `fontFace` declarations in a theme's `theme.json` file to generate the `@font-face` styles for both the editor and front-end.

Design notes:
* It is not a public API, but rather an internal, Core-only handler.
* It is a stopgap implementation that will be replaced when the public Webfonts API is introduced in Core.
* The code design is intentional, albeit funky, with the purpose of avoiding backwards-compatibility issues when the public Webfonts API is introduced in Core.
   * It hides the inter-workings.
   * Does not exposing API ins and outs for external consumption.
   * Only works for `theme.json`.
   * Does not provide registration or enqueuing access for plugins.

For more context on the decision to include this stopgap and the Webfonts API, see:
* Core's PR 40493 https://github.com/WordPress/gutenberg/pull/40493
* Gutenberg's tracking issue 40472 https://github.com/WordPress/gutenberg/issues/40472

Props aristath, hellofromTonya, peterwilsoncc, costdev, jffng, zieladam, gziolo, bph, jonoaldersonwp, desrosj.

See #55567, #46370.

git-svn-id: https://develop.svn.wordpress.org/trunk@53282 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Tonya Mork
2022-04-26 14:46:37 +00:00
parent 60cb20c4cc
commit 02414638ce
14 changed files with 855 additions and 0 deletions

View File

@@ -0,0 +1 @@
<?php

View File

@@ -0,0 +1,4 @@
<?php
/**
* Block theme.
*/

View File

@@ -0,0 +1,7 @@
/*
Theme Name: Empty `fontFace` in theme.json - no webfonts defined
Theme URI: https://wordpress.org/
Description: For testing purposes only.
Version: 1.0.0
Text Domain: empty-fontface-theme
*/

View File

@@ -0,0 +1,3 @@
<!-- wp:paragraph -->
<p>Index Template</p>
<!-- /wp:paragraph -->

View File

@@ -0,0 +1,88 @@
{
"version": 2,
"customTemplates": [
{
"name": "blank",
"title": "Blank",
"postTypes": [
"page",
"post"
]
}
],
"settings": {
"appearanceTools": true,
"color": {
"duotone": [],
"gradients": [],
"palette": []
},
"custom": {},
"spacing": {
"units": [
"%",
"px",
"em",
"rem",
"vh",
"vw"
]
},
"typography": {
"dropCap": false,
"fontFamilies": [
{
"fontFamily": "Roboto",
"name": "Roboto",
"slug": "roboto",
"fontFace": []
}
],
"fontSizes": [
{
"size": "1rem",
"slug": "small"
},
{
"size": "1.125rem",
"slug": "medium"
},
{
"size": "1.75rem",
"slug": "large"
},
{
"size": "clamp(1.75rem, 3vw, 2.25rem)",
"slug": "x-large"
}
]
},
"layout": {
"contentSize": "650px",
"wideSize": "1000px"
}
},
"styles": {
"blocks": {},
"color": {
"background": "var(--wp--preset--color--background)",
"text": "var(--wp--preset--color--foreground)"
},
"elements": {},
"spacing": {
"blockGap": "1.5rem"
},
"typography": {
"fontFamily": "var(--wp--preset--font-family--system-font)",
"lineHeight": "var(--wp--custom--typography--line-height--normal)",
"fontSize": "var(--wp--preset--font-size--medium)"
}
},
"templateParts": [
{
"name": "header",
"title": "Header",
"area": "header"
}
]
}

View File

@@ -0,0 +1 @@
<?php

View File

@@ -0,0 +1,4 @@
<?php
/**
* Block theme.
*/

View File

@@ -0,0 +1,7 @@
/*
Theme Name: Webfonts theme
Theme URI: https://wordpress.org/
Description: For testing purposes only.
Version: 1.0.0
Text Domain: webfonts-theme
*/

View File

@@ -0,0 +1,3 @@
<!-- wp:paragraph -->
<p>Index Template</p>
<!-- /wp:paragraph -->

View File

@@ -0,0 +1,103 @@
{
"version": 2,
"customTemplates": [
{
"name": "blank",
"title": "Blank",
"postTypes": [
"page",
"post"
]
}
],
"settings": {
"appearanceTools": true,
"color": {
"duotone": [],
"gradients": [],
"palette": []
},
"custom": {},
"spacing": {
"units": [
"%",
"px",
"em",
"rem",
"vh",
"vw"
]
},
"typography": {
"dropCap": false,
"fontFamilies": [
{
"fontFamily": "\"Source Serif Pro\", serif",
"name": "Source Serif Pro",
"slug": "source-serif-pro",
"fontFace": [
{
"fontFamily": "Source Serif Pro",
"fontWeight": "200 900",
"fontStyle": "normal",
"fontStretch": "normal",
"src": [ "file:./assets/fonts/SourceSerif4Variable-Roman.ttf.woff2" ]
},
{
"fontFamily": "Source Serif Pro",
"fontWeight": "200 900",
"fontStyle": "italic",
"fontStretch": "normal",
"src": [ "file:./assets/fonts/SourceSerif4Variable-Italic.ttf.woff2" ]
}
]
}
],
"fontSizes": [
{
"size": "1rem",
"slug": "small"
},
{
"size": "1.125rem",
"slug": "medium"
},
{
"size": "1.75rem",
"slug": "large"
},
{
"size": "clamp(1.75rem, 3vw, 2.25rem)",
"slug": "x-large"
}
]
},
"layout": {
"contentSize": "650px",
"wideSize": "1000px"
}
},
"styles": {
"blocks": {},
"color": {
"background": "var(--wp--preset--color--background)",
"text": "var(--wp--preset--color--foreground)"
},
"elements": {},
"spacing": {
"blockGap": "1.5rem"
},
"typography": {
"fontFamily": "var(--wp--preset--font-family--system-font)",
"lineHeight": "var(--wp--custom--typography--line-height--normal)",
"fontSize": "var(--wp--preset--font-size--medium)"
}
},
"templateParts": [
{
"name": "header",
"title": "Header",
"area": "header"
}
]
}

View File

@@ -165,6 +165,8 @@ class Tests_Theme_ThemeDir extends WP_UnitTestCase {
'Block Theme Child Theme',
'Block Theme [0.4.0]',
'Block Theme [1.0.0] in subdirectory',
'Webfonts theme',
'Empty `fontFace` in theme.json - no webfonts defined',
);
sort( $theme_names );

View File

@@ -0,0 +1,135 @@
<?php
/**
* Enqueue only webfonts listed in theme.json
*
* @package WordPress
*/
/**
* Integration tests for the theme.json webfonts handler.
*
* @group webfonts
* @group themes
* @covers _wp_theme_json_webfonts_handler
*/
class Test_WebfontsApi_WpThemeJsonWebfontsHandler extends WP_UnitTestCase {
/**
* WP_Styles instance reference
*
* @var WP_Styles
*/
private $orig_wp_styles;
/**
* Theme root path.
*
* @var string
*/
private $theme_root;
/**
* The old theme root path.
*
* @var string
*/
private $orig_theme_dir;
public function set_up() {
parent::set_up();
global $wp_styles;
$this->orig_wp_styles = $wp_styles;
$wp_styles = null;
$this->theme_root = realpath( DIR_TESTDATA . '/themedir1' );
$this->orig_theme_dir = $GLOBALS['wp_theme_directories'];
// /themes is necessary as theme.php functions assume /themes is the root if there is only one root.
$GLOBALS['wp_theme_directories'] = array( WP_CONTENT_DIR . '/themes', $this->theme_root );
$theme_root_callback = function () {
return $this->theme_root;
};
add_filter( 'theme_root', $theme_root_callback );
add_filter( 'stylesheet_root', $theme_root_callback );
add_filter( 'template_root', $theme_root_callback );
// Clear caches.
wp_clean_themes_cache();
unset( $GLOBALS['wp_themes'] );
}
public function tear_down() {
global $wp_styles;
$wp_styles = $this->orig_wp_styles;
// Restore the original theme directory setup.
$GLOBALS['wp_theme_directories'] = $this->orig_theme_dir;
wp_clean_themes_cache();
unset( $GLOBALS['wp_themes'] );
parent::tear_down();
}
/**
* @ticket 55567
* @ticket 46370
*/
public function test_font_face_generated_from_themejson() {
$this->setup_theme_and_test( 'webfonts-theme' );
$expected = <<<EOF
<style id='wp-webfonts-inline-css' type='text/css'>
@font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('THEME_ROOT_URL/assets/fonts/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');font-stretch:normal;}@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('THEME_ROOT_URL/assets/fonts/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');font-stretch:normal;}
</style>
EOF;
$expected = str_replace( 'THEME_ROOT_URL', get_stylesheet_directory_uri(), $expected );
$this->assertStringContainsString(
$expected,
get_echo( 'wp_print_styles' )
);
}
/**
* @dataProvider data_font_face_not_generated
*
* @ticket 55567
* @ticket 46370
*/
public function test_font_face_not_generated( $theme_name ) {
$this->setup_theme_and_test( $theme_name );
$actual = get_echo( 'wp_print_styles' );
$this->assertStringNotContainsString( "<style id='wp-webfonts-inline-css", $actual );
$this->assertStringNotContainsString( '@font-face', $actual );
}
/**
* Data provider for unhappy paths.
*
* @return string[][]
*/
public function data_font_face_not_generated() {
return array(
'classic theme with no theme.json' => array( 'default' ),
'no "fontFace" in theme.json' => array( 'block-theme' ),
'empty "fontFace" in theme.json' => array( 'empty-fontface-theme' ),
);
}
/**
* Sets up the theme and test.
*
* @param string $theme_name Name of the theme to switch to for the test.
*/
private function setup_theme_and_test( $theme_name ) {
switch_theme( $theme_name );
do_action( 'after_setup_theme' );
WP_Theme_JSON_Resolver::clean_cached_data();
do_action( 'wp_loaded' );
do_action( 'wp_enqueue_scripts' );
}
}