Add Flex Renderer test
[MAILPOET-5644]
This commit is contained in:
committed by
Jan Lysý
parent
820af6454b
commit
c5ad2405aa
@@ -28,9 +28,10 @@ class FlexLayoutRenderer {
|
|||||||
$outputHtml .= '</tr></table></div>
|
$outputHtml .= '</tr></table></div>
|
||||||
<!--[if mso | IE]></td></tr></table><![endif]-->';
|
<!--[if mso | IE]></td></tr></table><![endif]-->';
|
||||||
|
|
||||||
$styles = wp_style_engine_get_styles($parsedBlock['attrs']['style'])['css'];
|
$wpGeneratedStyles = wp_style_engine_get_styles($parsedBlock['attrs']['style'] ?? []);
|
||||||
$justify = $parsedBlock['attrs']['layout']['justifyContent'] ?? 'left';
|
$styles = $wpGeneratedStyles['css'] ?? '';
|
||||||
$styles .= 'text-align: ' . esc_attr($justify);
|
$justify = esc_attr($parsedBlock['attrs']['layout']['justifyContent'] ?? 'left');
|
||||||
|
$styles .= 'text-align: ' . $justify;
|
||||||
$outputHtml = str_replace('{style}', $styles, $outputHtml);
|
$outputHtml = str_replace('{style}', $styles, $outputHtml);
|
||||||
$outputHtml = str_replace('{align}', $justify, $outputHtml);
|
$outputHtml = str_replace('{align}', $justify, $outputHtml);
|
||||||
|
|
||||||
@@ -39,7 +40,6 @@ class FlexLayoutRenderer {
|
|||||||
|
|
||||||
private function computeWidthsForFlexLayout(array $parsedBlock, SettingsController $settingsController): array {
|
private function computeWidthsForFlexLayout(array $parsedBlock, SettingsController $settingsController): array {
|
||||||
$blocksCount = count($parsedBlock['innerBlocks']);
|
$blocksCount = count($parsedBlock['innerBlocks']);
|
||||||
$totalSetWidth = 0; // Total width set by user. Excludes items that have no width set
|
|
||||||
$totalUsedWidth = 0; // Total width assuming items without set width would consume proportional width
|
$totalUsedWidth = 0; // Total width assuming items without set width would consume proportional width
|
||||||
$parentWidth = $settingsController->parseNumberFromStringWithPixels($parsedBlock['email_attrs']['width'] ?? SettingsController::EMAIL_WIDTH);
|
$parentWidth = $settingsController->parseNumberFromStringWithPixels($parsedBlock['email_attrs']['width'] ?? SettingsController::EMAIL_WIDTH);
|
||||||
$flexGap = $settingsController->parseNumberFromStringWithPixels(SettingsController::FLEX_GAP);
|
$flexGap = $settingsController->parseNumberFromStringWithPixels(SettingsController::FLEX_GAP);
|
||||||
@@ -48,7 +48,6 @@ class FlexLayoutRenderer {
|
|||||||
foreach ($innerBlocks as $key => $block) {
|
foreach ($innerBlocks as $key => $block) {
|
||||||
$blockWidthPercent = ($block['attrs']['width'] ?? 0) ? intval($block['attrs']['width']) : 0;
|
$blockWidthPercent = ($block['attrs']['width'] ?? 0) ? intval($block['attrs']['width']) : 0;
|
||||||
$blockWidth = floor($parentWidth * ($blockWidthPercent / 100));
|
$blockWidth = floor($parentWidth * ($blockWidthPercent / 100));
|
||||||
$totalSetWidth += $blockWidth;
|
|
||||||
// If width is not set, we assume it's 25% of the parent width
|
// If width is not set, we assume it's 25% of the parent width
|
||||||
$totalUsedWidth += $blockWidth ?: floor($parentWidth * (25 / 100));
|
$totalUsedWidth += $blockWidth ?: floor($parentWidth * (25 / 100));
|
||||||
|
|
||||||
@@ -56,22 +55,32 @@ class FlexLayoutRenderer {
|
|||||||
$innerBlocks[$key]['email_attrs']['layout_width'] = null; // Will be rendered as auto
|
$innerBlocks[$key]['email_attrs']['layout_width'] = null; // Will be rendered as auto
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// How many percent of width we will strip to keep some space fot the gap
|
$innerBlocks[$key]['email_attrs']['layout_width'] = $this->getWidthWithoutGap($blockWidth, $flexGap, $blockWidthPercent) . 'px';
|
||||||
// Todo add more precise comment
|
|
||||||
$widthGapReduction = $flexGap * ((100 - $blockWidthPercent) / 100);
|
|
||||||
$innerBlocks[$key]['email_attrs']['layout_width'] = floor($blockWidth - $widthGapReduction) . 'px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When there is only one block, or percentage is set reasonably we don't need to adjust and just render as set by user
|
// When there is only one block, or percentage is set reasonably we don't need to adjust and just render as set by user
|
||||||
if ($blocksCount <= 1 || ($totalSetWidth < $parentWidth)) {
|
if ($blocksCount <= 1 || ($totalUsedWidth <= $parentWidth)) {
|
||||||
return $innerBlocks;
|
return $innerBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($innerBlocks as $key => $block) {
|
foreach ($innerBlocks as $key => $block) {
|
||||||
$proportionalSpaceOverflow = $parentWidth / $totalUsedWidth;
|
$proportionalSpaceOverflow = $parentWidth / $totalUsedWidth;
|
||||||
$blockWidth = $block['email_attrs']['layout_width'] ? $settingsController->parseNumberFromStringWithPixels($block['email_attrs']['layout_width']) : 0;
|
$blockWidth = $block['email_attrs']['layout_width'] ? $settingsController->parseNumberFromStringWithPixels($block['email_attrs']['layout_width']) : 0;
|
||||||
$innerBlocks[$key]['email_attrs']['layout_width'] = $blockWidth ? intval(round($blockWidth * $proportionalSpaceOverflow)) . 'px' : null;
|
$blockProportionalWidth = $blockWidth * $proportionalSpaceOverflow;
|
||||||
|
$blockProportionalPercentage = ($blockProportionalWidth / $parentWidth) * 100;
|
||||||
|
$innerBlocks[$key]['email_attrs']['layout_width'] = $blockWidth ? $this->getWidthWithoutGap($blockProportionalWidth, $flexGap, $blockProportionalPercentage) . 'px' : null;
|
||||||
}
|
}
|
||||||
return $innerBlocks;
|
return $innerBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much of width we will strip to keep some space for the gap
|
||||||
|
* This is computed based on CSS rule used in the editor:
|
||||||
|
* For block with width set to X percent
|
||||||
|
* width: calc(X% - (var(--wp--style--block-gap) * (100 - X)/100)));
|
||||||
|
*/
|
||||||
|
private function getWidthWithoutGap(float $blockWidth, float $flexGap, float $blockWidthPercent): int {
|
||||||
|
$widthGapReduction = $flexGap * ((100 - $blockWidthPercent) / 100);
|
||||||
|
return intval(floor($blockWidth - $widthGapReduction));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -123,8 +123,8 @@ class SettingsController {
|
|||||||
$contentVariables = 'body {';
|
$contentVariables = 'body {';
|
||||||
$contentVariables .= 'padding-bottom: var(--wp--style--root--padding-bottom);';
|
$contentVariables .= 'padding-bottom: var(--wp--style--root--padding-bottom);';
|
||||||
$contentVariables .= 'padding-top: var(--wp--style--root--padding-top);';
|
$contentVariables .= 'padding-top: var(--wp--style--root--padding-top);';
|
||||||
|
$contentVariables .= '--wp--style--block-gap:' . self::FLEX_GAP . ';';
|
||||||
$contentVariables .= '}';
|
$contentVariables .= '}';
|
||||||
$contentVariables .= '--mp-flex-layout-gap:' . self::FLEX_GAP . ';';
|
|
||||||
|
|
||||||
$settings = array_merge($coreDefaultSettings, self::DEFAULT_SETTINGS);
|
$settings = array_merge($coreDefaultSettings, self::DEFAULT_SETTINGS);
|
||||||
$settings['allowedBlockTypes'] = self::ALLOWED_BLOCK_TYPES;
|
$settings['allowedBlockTypes'] = self::ALLOWED_BLOCK_TYPES;
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:where(body .is-layout-flex) {
|
:where(body .is-layout-flex) {
|
||||||
gap: var(--mp-flex-layout-gap, 16px);
|
gap: var(--wp--style--block-gap, 16px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-mobile-preview .is-layout-email-flex {
|
.is-mobile-preview .is-layout-email-flex {
|
||||||
|
@@ -6,10 +6,6 @@ use MailPoet\EmailEditor\Engine\SettingsController;
|
|||||||
|
|
||||||
class DummyBlockRenderer implements BlockRenderer {
|
class DummyBlockRenderer implements BlockRenderer {
|
||||||
public function render(string $blockContent, array $parsedBlock, SettingsController $settingsController): string {
|
public function render(string $blockContent, array $parsedBlock, SettingsController $settingsController): string {
|
||||||
if (!isset($parsedBlock['innerBlocks']) || empty($parsedBlock['innerBlocks'])) {
|
return $parsedBlock['innerHtml'];
|
||||||
return $parsedBlock['innerHTML'];
|
|
||||||
}
|
|
||||||
// Wrapper is rendered in parent Columns block because it needs to operate with columns count etc.
|
|
||||||
return '[' . $this->render('', $parsedBlock['innerBlocks'], $settingsController) . ']';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,234 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace MailPoet\EmailEditor\Engine\Renderer\Layout;
|
||||||
|
|
||||||
|
use MailPoet\EmailEditor\Engine\Renderer\BlocksRegistry;
|
||||||
|
use MailPoet\EmailEditor\Engine\Renderer\DummyBlockRenderer;
|
||||||
|
use MailPoet\EmailEditor\Engine\SettingsController;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../DummyBlockRenderer.php';
|
||||||
|
|
||||||
|
class FlexLayoutRendererTest extends \MailPoetTest {
|
||||||
|
|
||||||
|
/** @var BlocksRegistry */
|
||||||
|
private $registry;
|
||||||
|
|
||||||
|
/** @var FlexLayoutRenderer */
|
||||||
|
private $renderer;
|
||||||
|
|
||||||
|
/** @var SettingsController */
|
||||||
|
private $settingsController;
|
||||||
|
|
||||||
|
public function _before() {
|
||||||
|
parent::_before();
|
||||||
|
$this->settingsController = new SettingsController();
|
||||||
|
$this->registry = new BlocksRegistry($this->settingsController);
|
||||||
|
$this->renderer = new FlexLayoutRenderer();
|
||||||
|
$this->registry->addBlockRenderer('dummy/block', new DummyBlockRenderer());
|
||||||
|
register_block_type('dummy/block', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItRendersInnerBlocks() {
|
||||||
|
$parsedBlock = [
|
||||||
|
'innerBlocks' => [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 2',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'email_attrs' => [],
|
||||||
|
];
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
verify($output)->stringContainsString('Dummy 1');
|
||||||
|
verify($output)->stringContainsString('Dummy 2');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItHandlesJustification() {
|
||||||
|
$parsedBlock = [
|
||||||
|
'innerBlocks' => [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'email_attrs' => [],
|
||||||
|
];
|
||||||
|
// Default justification is left
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
verify($output)->stringContainsString('text-align: left');
|
||||||
|
verify($output)->stringContainsString('align="left"');
|
||||||
|
// Right justification
|
||||||
|
$parsedBlock['attrs']['layout']['justifyContent'] = 'right';
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
verify($output)->stringContainsString('text-align: right');
|
||||||
|
verify($output)->stringContainsString('align="right"');
|
||||||
|
// Center justification
|
||||||
|
$parsedBlock['attrs']['layout']['justifyContent'] = 'center';
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
verify($output)->stringContainsString('text-align: center');
|
||||||
|
verify($output)->stringContainsString('align="center"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItEscapesAttributes() {
|
||||||
|
$parsedBlock = [
|
||||||
|
'innerBlocks' => [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'email_attrs' => [],
|
||||||
|
];
|
||||||
|
$parsedBlock['attrs']['layout']['justifyContent'] = '"> <script>alert("XSS")</script><div style="text-align: right';
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
verify($output)->stringNotContainsString('<script>alert("XSS")</script>');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInComputesProperWidthsForReasonableSettings() {
|
||||||
|
$parsedBlock = [
|
||||||
|
'innerBlocks' => [],
|
||||||
|
'email_attrs' => [
|
||||||
|
'width' => '640px',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// 50% and 25%
|
||||||
|
$parsedBlock['innerBlocks'] = [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
'attrs' => ['width' => '50'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 2',
|
||||||
|
'attrs' => ['width' => '25'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
$flexItems = $this->getFlexItemsFromOutput($output);
|
||||||
|
verify($flexItems[0])->stringContainsString('width:312px;');
|
||||||
|
verify($flexItems[1])->stringContainsString('width:148px;');
|
||||||
|
|
||||||
|
// 25% and 25% and auto
|
||||||
|
$parsedBlock['innerBlocks'] = [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
'attrs' => ['width' => '25'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 2',
|
||||||
|
'attrs' => ['width' => '25'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 3',
|
||||||
|
'attrs' => [],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
$flexItems = $this->getFlexItemsFromOutput($output);
|
||||||
|
verify($flexItems[0])->stringContainsString('width:148px;');
|
||||||
|
verify($flexItems[1])->stringContainsString('width:148px;');
|
||||||
|
verify($flexItems[2])->stringNotContainsString('width:');
|
||||||
|
|
||||||
|
// 50% and 50%
|
||||||
|
$parsedBlock['innerBlocks'] = [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
'attrs' => ['width' => '50'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 2',
|
||||||
|
'attrs' => ['width' => '50'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
$flexItems = $this->getFlexItemsFromOutput($output);
|
||||||
|
verify($flexItems[0])->stringContainsString('width:312px;');
|
||||||
|
verify($flexItems[1])->stringContainsString('width:312px;');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInComputesWidthsForStrangeSettingsValues() {
|
||||||
|
$parsedBlock = [
|
||||||
|
'innerBlocks' => [],
|
||||||
|
'email_attrs' => [
|
||||||
|
'width' => '640px',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
// 100% and 25%
|
||||||
|
$parsedBlock['innerBlocks'] = [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
'attrs' => ['width' => '100'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 2',
|
||||||
|
'attrs' => ['width' => '25'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
$flexItems = $this->getFlexItemsFromOutput($output);
|
||||||
|
verify($flexItems[0])->stringContainsString('width:508px;');
|
||||||
|
verify($flexItems[1])->stringContainsString('width:105px;');
|
||||||
|
|
||||||
|
// 100% and 100%
|
||||||
|
$parsedBlock['innerBlocks'] = [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
'attrs' => ['width' => '100'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 2',
|
||||||
|
'attrs' => ['width' => '100'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
$flexItems = $this->getFlexItemsFromOutput($output);
|
||||||
|
verify($flexItems[0])->stringContainsString('width:312px;');
|
||||||
|
verify($flexItems[1])->stringContainsString('width:312px;');
|
||||||
|
|
||||||
|
|
||||||
|
// 100% and auto
|
||||||
|
$parsedBlock['innerBlocks'] = [
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 1',
|
||||||
|
'attrs' => ['width' => '100'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'blockName' => 'dummy/block',
|
||||||
|
'innerHtml' => 'Dummy 2',
|
||||||
|
'attrs' => [],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$output = $this->renderer->renderInnerBlocksInLayout($parsedBlock, $this->settingsController);
|
||||||
|
$flexItems = $this->getFlexItemsFromOutput($output);
|
||||||
|
verify($flexItems[0])->stringContainsString('width:508px;');
|
||||||
|
verify($flexItems[1])->stringNotContainsString('width:');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFlexItemsFromOutput(string $output): array {
|
||||||
|
$matches = [];
|
||||||
|
preg_match_all('/<td class="layout-flex-item" style="(.*)">/', $output, $matches);
|
||||||
|
return explode('><', $matches[0][0] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function _after() {
|
||||||
|
parent::_after();
|
||||||
|
unregister_block_type('dummy/block');
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user