From b90a35d80a43f04aaf6ea62d4fe081190d82035e Mon Sep 17 00:00:00 2001 From: Rostislav Wolny Date: Fri, 19 Jan 2024 12:08:39 +0100 Subject: [PATCH] Add definition of default heading font-sizes and renderer support Initially, I tried to place the definition to theme.json (It could set it in styles.block.core/heading or styles.elements.h1...) It was not possible to use theme.json because of the fluid typography feature which, when enabled for a site, causes font sizes to being converted to the fluid definition (clamp(x, y, z)) and which is not usable for an email due to very little client support. We need to make some changes in Gutenberg to be able to disable the feature. Currently, the code for generating font sizes in CSS generated from the theme.json looks directly at the global settings of the site. [MAILPOET-5740] --- .../EmailEditor/Engine/SettingsController.php | 2 ++ .../Integrations/Core/Initializer.php | 14 ++++++++ .../Core/Renderer/Blocks/Heading.php | 18 ++++++---- .../EmailEditor/Integrations/Core/styles.css | 23 ++++++++++++ .../Core/Renderer/Blocks/HeadingTest.php | 8 +++++ .../Core/Renderer/RendererTest.php | 35 +++++++++++++------ 6 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 mailpoet/lib/EmailEditor/Integrations/Core/styles.css diff --git a/mailpoet/lib/EmailEditor/Engine/SettingsController.php b/mailpoet/lib/EmailEditor/Engine/SettingsController.php index 01d847a2d7..a92b713595 100644 --- a/mailpoet/lib/EmailEditor/Engine/SettingsController.php +++ b/mailpoet/lib/EmailEditor/Engine/SettingsController.php @@ -70,6 +70,8 @@ class SettingsController { ['css' => $flexEmailLayoutStyles], ]; + $settings['styles'] = apply_filters('mailpoet_email_editor_editor_styles', $settings['styles']); + $settings['__experimentalFeatures'] = $themeSettings; // Enable border radius, color, style and width where possible $settings['__experimentalFeatures']['border'] = [ diff --git a/mailpoet/lib/EmailEditor/Integrations/Core/Initializer.php b/mailpoet/lib/EmailEditor/Integrations/Core/Initializer.php index e773d78446..2a7d929b0b 100644 --- a/mailpoet/lib/EmailEditor/Integrations/Core/Initializer.php +++ b/mailpoet/lib/EmailEditor/Integrations/Core/Initializer.php @@ -9,6 +9,8 @@ class Initializer { public function initialize(): void { add_action('mailpoet_blocks_renderer_initialized', [$this, 'registerCoreBlocksRenderers'], 10, 1); add_filter('mailpoet_email_editor_theme_json', [$this, 'adjustThemeJson'], 10, 1); + add_filter('mailpoet_email_editor_editor_styles', [$this, 'addEditorStyles'], 10, 1); + add_filter('mailpoet_email_renderer_styles', [$this, 'addRendererStyles'], 10, 1); } /** @@ -35,4 +37,16 @@ class Initializer { $editorThemeJson->merge(new \WP_Theme_JSON($themeJson, 'default')); return $editorThemeJson; } + + public function addEditorStyles(array $styles) { + $declaration = (string)file_get_contents(dirname(__FILE__) . '/styles.css'); + $styles[] = ['css' => $declaration]; + return $styles; + } + + public function addRendererStyles(string $styles) { + $declaration = (string)file_get_contents(dirname(__FILE__) . '/styles.css'); + $styles .= $declaration; + return $styles; + } } diff --git a/mailpoet/lib/EmailEditor/Integrations/Core/Renderer/Blocks/Heading.php b/mailpoet/lib/EmailEditor/Integrations/Core/Renderer/Blocks/Heading.php index 6f4b146545..7e453867ae 100644 --- a/mailpoet/lib/EmailEditor/Integrations/Core/Renderer/Blocks/Heading.php +++ b/mailpoet/lib/EmailEditor/Integrations/Core/Renderer/Blocks/Heading.php @@ -9,7 +9,7 @@ use MailPoet\Util\Helpers; class Heading implements BlockRenderer { public function render(string $blockContent, array $parsedBlock, SettingsController $settingsController): string { $level = $parsedBlock['attrs']['level'] ?? 2; // default level is 2 - $blockContent = $this->removePaddingFromElement($blockContent, ['tag_name' => "h{$level}"]); + $blockContent = $this->adjustStyleAttribute($blockContent, $parsedBlock, $settingsController, ['tag_name' => "h{$level}"]); return str_replace('{heading_content}', $blockContent, $this->getBlockWrapper($parsedBlock, $settingsController)); } @@ -17,7 +17,7 @@ class Heading implements BlockRenderer { * Based on MJML */ private function getBlockWrapper(array $parsedBlock, SettingsController $settingsController): string { - $themeData = $settingsController->getTheme()->get_data(); + $availableStylesheets = $settingsController->getAvailableStylesheets(); // Styles for padding need to be set on the wrapping table cell due to support in Outlook @@ -39,10 +39,6 @@ class Heading implements BlockRenderer { $styles[$property] = $value; } - if (!isset($styles['font-size'])) { - $styles['font-size'] = $themeData['styles']['typography']['fontSize']; - } - $styles = array_merge($styles, $this->fetchStylesFromBlockAttrs($availableStylesheets, $parsedBlock['attrs'])); return ' @@ -113,13 +109,21 @@ class Heading implements BlockRenderer { } /** + * 1) We need to remove padding because we render padding on wrapping table cell + * 2) We also need to replace font-size to avoid clamp() because clamp() is not supported in many email clients. + * The font size values is automatically converted to clamp() when WP site theme is configured to use fluid layouts. + * Currently (WP 6.4), there is no way to disable this behavior. * @param array{tag_name: string, class_name?: string} $tag */ - private function removePaddingFromElement($blockContent, array $tag): string { + private function adjustStyleAttribute($blockContent, array $parsedBlock, SettingsController $settingsController, array $tag): string { $html = new \WP_HTML_Tag_Processor($blockContent); + $themeData = $settingsController->getTheme()->get_data(); + $fontSize = 'font-size:' . ($parsedBlock['email_attrs']['font-size'] ?? $themeData['styles']['typography']['fontSize']) . ';'; + if ($html->next_tag($tag)) { $elementStyle = $html->get_attribute('style') ?? ''; $elementStyle = preg_replace('/padding.*:.?[0-9]+px;?/', '', $elementStyle); + $elementStyle = preg_replace('/font-size:[^;]+;?/', $fontSize, $elementStyle); $html->set_attribute('style', $elementStyle); $blockContent = $html->get_updated_html(); } diff --git a/mailpoet/lib/EmailEditor/Integrations/Core/styles.css b/mailpoet/lib/EmailEditor/Integrations/Core/styles.css new file mode 100644 index 0000000000..26ae5a28ef --- /dev/null +++ b/mailpoet/lib/EmailEditor/Integrations/Core/styles.css @@ -0,0 +1,23 @@ +h1 { + font-size: 48px; +} + +h2 { + font-size: 42px; +} + +h3 { + font-size: 36px; +} + +h4 { + font-size: 26px; +} + +h5 { + font-size: 20px; +} + +h6 { + font-size: 13px; +} diff --git a/mailpoet/tests/integration/EmailEditor/Integration/Core/Renderer/Blocks/HeadingTest.php b/mailpoet/tests/integration/EmailEditor/Integration/Core/Renderer/Blocks/HeadingTest.php index 4e79ebe1d4..55ecde9c23 100644 --- a/mailpoet/tests/integration/EmailEditor/Integration/Core/Renderer/Blocks/HeadingTest.php +++ b/mailpoet/tests/integration/EmailEditor/Integration/Core/Renderer/Blocks/HeadingTest.php @@ -20,11 +20,13 @@ class HeadingTest extends \MailPoetTest { 'style' => [ 'typography' => [ 'textTransform' => 'lowercase', + 'fontSize' => '24px', ], ], ], 'email_attrs' => [ 'width' => '640px', + 'font-size' => '24px', ], 'innerBlocks' => [], 'innerHTML' => '

This is Heading 1

', @@ -46,6 +48,7 @@ class HeadingTest extends \MailPoetTest { $rendered = $this->headingRenderer->render('

This is Heading 1

', $this->parsedHeading, $this->settingsController); verify($rendered)->stringContainsString('This is Heading 1'); verify($rendered)->stringContainsString('width: 100%;'); + verify($rendered)->stringContainsString('font-size:24px;'); verify($rendered)->stringNotContainsString('width:640px;'); } @@ -56,4 +59,9 @@ class HeadingTest extends \MailPoetTest { verify($rendered)->stringContainsString('text-transform:lowercase;'); verify($rendered)->stringContainsString('text-align:center;'); } + + public function testItReplacesFluidFontSizeInContent(): void { + $rendered = $this->headingRenderer->render('

This is Heading 1

', $this->parsedHeading, $this->settingsController); + verify($rendered)->stringContainsString('font-size:24px'); + } } diff --git a/mailpoet/tests/integration/EmailEditor/Integration/Core/Renderer/RendererTest.php b/mailpoet/tests/integration/EmailEditor/Integration/Core/Renderer/RendererTest.php index f7424a0726..38f95c62bb 100644 --- a/mailpoet/tests/integration/EmailEditor/Integration/Core/Renderer/RendererTest.php +++ b/mailpoet/tests/integration/EmailEditor/Integration/Core/Renderer/RendererTest.php @@ -22,19 +22,32 @@ class RendererTest extends \MailPoetTest { 'post_content' => '', ]); $rendered = $this->renderer->render($emailPost, 'Subject', '', 'en'); - $doc = new \DOMDocument(); - $doc->loadHTML($rendered['html']); - $xpath = new \DOMXPath($doc); - $nodes = $xpath->query('//td[contains(@class, "wp-block-button")]'); - $button = null; - if (($nodes instanceof \DOMNodeList) && $nodes->length > 0) { - $button = $nodes->item(0); - } - $this->assertInstanceOf(\DOMElement::class, $button); - $this->assertInstanceOf(\DOMDocument::class, $button->ownerDocument); - $buttonHtml = $button->ownerDocument->saveHTML($button); + $buttonHtml = $this->extractBlockHtml($rendered['html'], 'wp-block-button', 'td'); verify($buttonHtml)->stringContainsString('color:#ffffff'); verify($buttonHtml)->stringContainsString('padding:.7em 1.4em'); verify($buttonHtml)->stringContainsString('background:#32373c'); } + + public function testItInlinesHeadingFontSize() { + $emailPost = new \WP_Post((object)[ + 'post_content' => '

Hello

', + ]); + $rendered = $this->renderer->render($emailPost, 'Subject', '', 'en'); + $headingHtml = $this->extractBlockHtml($rendered['html'], 'wp-block-heading', 'h1'); + verify($headingHtml)->stringContainsString('font-size:48px'); // large is 48px + } + + private function extractBlockHtml(string $html, string $blockClass, string $tag): string { + $doc = new \DOMDocument(); + $doc->loadHTML($html); + $xpath = new \DOMXPath($doc); + $nodes = $xpath->query('//' . $tag . '[contains(@class, "' . $blockClass . '")]'); + $block = null; + if (($nodes instanceof \DOMNodeList) && $nodes->length > 0) { + $block = $nodes->item(0); + } + $this->assertInstanceOf(\DOMElement::class, $block); + $this->assertInstanceOf(\DOMDocument::class, $block->ownerDocument); + return (string)$block->ownerDocument->saveHTML($block); + } }