Add preprocessor for calculating block width

[MAILPOET-5591]
This commit is contained in:
Jan Lysý
2023-10-13 19:53:31 +02:00
committed by Jan Lysý
parent 62ea18f8be
commit 2b69acf0d5
5 changed files with 274 additions and 2 deletions

View File

@@ -336,6 +336,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\EmailEditor\Engine\EmailEditor::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\EmailApiController::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\SettingsController::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\Preprocessors\BlocksWidthPreprocessor::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\Preprocessors\CleanupPreprocessor::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\Preprocessors\TopLevelPreprocessor::class)->setPublic(true);
$container->autowire(\MailPoet\EmailEditor\Engine\Renderer\Renderer::class)->setPublic(true);

View File

@@ -2,6 +2,7 @@
namespace MailPoet\EmailEditor\Engine\Renderer;
use MailPoet\EmailEditor\Engine\Renderer\Preprocessors\BlocksWidthPreprocessor;
use MailPoet\EmailEditor\Engine\Renderer\Preprocessors\CleanupPreprocessor;
use MailPoet\EmailEditor\Engine\Renderer\Preprocessors\Preprocessor;
use MailPoet\EmailEditor\Engine\Renderer\Preprocessors\TopLevelPreprocessor;
@@ -12,10 +13,12 @@ class PreprocessManager {
public function __construct(
CleanupPreprocessor $cleanupPreprocessor,
TopLevelPreprocessor $topLevelPreprocessor
TopLevelPreprocessor $topLevelPreprocessor,
BlocksWidthPreprocessor $blocksWidthPreprocessor
) {
$this->registerPreprocessor($cleanupPreprocessor);
$this->registerPreprocessor($topLevelPreprocessor);
$this->registerPreprocessor($blocksWidthPreprocessor);
}
/**

View File

@@ -0,0 +1,64 @@
<?php declare(strict_types = 1);
namespace MailPoet\EmailEditor\Engine\Renderer\Preprocessors;
/**
* This class sets the width of the blocks based on the layout width or column count.
* The final width in pixels is stored in the email_attrs array because we would like to avoid changing the original attributes.
*/
class BlocksWidthPreprocessor implements Preprocessor {
public function preprocess(array $parsedBlocks, array $layoutStyles): array {
$layoutWidth = $layoutStyles['width'];
// Subtract padding from the width of the layout element
$layoutWidth -= $layoutStyles['padding']['left'] ?? 0;
$layoutWidth -= $layoutStyles['padding']['right'] ?? 0;
foreach ($parsedBlocks as $key => $block) {
$width = $this->convertWithToPixels($block['attrs']['width'] ?? '100%', $layoutWidth);
if ($block['blockName'] === 'core/columns') {
$block['innerBlocks'] = $this->addMissingColumnWidths($block['innerBlocks']);
}
// Copy layout styles and update width and padding
$modifiedLayoutStyles = $layoutStyles;
$modifiedLayoutStyles['width'] = $width;
$modifiedLayoutStyles['padding']['left'] = $this->parseNumberFromStringWithPixels($block['attrs']['style']['spacing']['padding']['left'] ?? '0px');
$modifiedLayoutStyles['padding']['right'] = $this->parseNumberFromStringWithPixels($block['attrs']['style']['spacing']['padding']['right'] ?? '0px');
// Set current block values and reassign it to $parsedBlocks
$block['email_attrs']['width'] = $width;
$block['innerBlocks'] = $this->preprocess($block['innerBlocks'], $modifiedLayoutStyles);
$parsedBlocks[$key] = $block;
}
return $parsedBlocks;
}
// TODO: We could add support for other units like em, rem, etc.
private function convertWithToPixels(string $currentWidth, float $layoutWidth): float {
$width = $layoutWidth;
if (strpos($currentWidth, '%') !== false) {
$width = (float)str_replace('%', '', $currentWidth);
$width = round($width / 100 * $layoutWidth);
} elseif (strpos($currentWidth, 'px') !== false) {
$width = $this->parseNumberFromStringWithPixels($currentWidth);
}
return $width;
}
private function parseNumberFromStringWithPixels(string $string): float {
return (float)str_replace('px', '', $string);
}
private function addMissingColumnWidths(array $columns): array {
$columnsCount = count($columns);
$defaultColumnsWidth = round(100 / $columnsCount, 2);
foreach ($columns as $key => $column) {
if (!isset($column['attrs']['width'])) {
$columns[$key]['attrs']['width'] = "{$defaultColumnsWidth}%";
}
}
return $columns;
}
}

View File

@@ -3,6 +3,7 @@
namespace unit\EmailEditor\Engine\Renderer;
use MailPoet\EmailEditor\Engine\Renderer\PreprocessManager;
use MailPoet\EmailEditor\Engine\Renderer\Preprocessors\BlocksWidthPreprocessor;
use MailPoet\EmailEditor\Engine\Renderer\Preprocessors\CleanupPreprocessor;
use MailPoet\EmailEditor\Engine\Renderer\Preprocessors\TopLevelPreprocessor;
@@ -24,10 +25,13 @@ class PreprocessManagerTest extends \MailPoetUnitTest {
$cleanup = $this->createMock(CleanupPreprocessor::class);
$cleanup->expects($this->once())->method('preprocess')->willReturn([]);
$blocksWidth = $this->createMock(BlocksWidthPreprocessor::class);
$blocksWidth->expects($this->once())->method('preprocess')->willReturn([]);
$secondPreprocessor = $this->createMock(TopLevelPreprocessor::class);
$secondPreprocessor->expects($this->once())->method('preprocess')->willReturn([]);
$preprocessManager = new PreprocessManager($cleanup, $topLevel);
$preprocessManager = new PreprocessManager($cleanup, $topLevel, $blocksWidth);
$preprocessManager->registerPreprocessor($secondPreprocessor);
expect($preprocessManager->preprocess([], $layoutStyles))->equals([]);
}

View File

@@ -0,0 +1,200 @@
<?php declare(strict_types = 1);
namespace unit\EmailEditor\Engine\Renderer\Preprocessors;
use MailPoet\EmailEditor\Engine\Renderer\Preprocessors\BlocksWidthPreprocessor;
class BlocksWidthPreprocessorTest extends \MailPoetUnitTest {
/** @var BlocksWidthPreprocessor */
private $preprocessor;
public function _before() {
parent::_before();
$this->preprocessor = new BlocksWidthPreprocessor();
}
public function testItCalculatesWidthWithoutPadding(): void {
$blocks = [[
'blockName' => 'core/columns',
'attrs' => [],
'innerBlocks' => [
[
'blockName' => 'core/column',
'attrs' => [
'width' => '50%',
],
'innerBlocks' => [],
],
[
'blockName' => 'core/column',
'attrs' => [
'width' => '25%',
],
'innerBlocks' => [],
],
[
'blockName' => 'core/column',
'attrs' => [
'width' => '100px',
],
'innerBlocks' => [],
],
],
]];
$result = $this->preprocessor->preprocess($blocks, ['width' => 660, 'padding' => ['left' => 0, 'right' => 0]]);
$result = $result[0];
expect($result['email_attrs']['width'])->equals(660);
expect($result['innerBlocks'])->count(3);
expect($result['innerBlocks'][0]['email_attrs']['width'])->equals(330); // 660 * 0.5
expect($result['innerBlocks'][1]['email_attrs']['width'])->equals(165); // 660 * 0.25
expect($result['innerBlocks'][2]['email_attrs']['width'])->equals(100);
}
public function testItCalculatesWidthWithLayoutPadding(): void {
$blocks = [[
'blockName' => 'core/columns',
'attrs' => [],
'innerBlocks' => [
[
'blockName' => 'core/column',
'attrs' => [
'width' => '33%',
],
'innerBlocks' => [],
],
[
'blockName' => 'core/column',
'attrs' => [
'width' => '100px',
],
'innerBlocks' => [],
],
[
'blockName' => 'core/column',
'attrs' => [
'width' => '20%',
],
'innerBlocks' => [],
],
],
]];
$result = $this->preprocessor->preprocess($blocks, ['width' => 600, 'padding' => ['left' => 20, 'right' => 20]]);
$result = $result[0];
expect($result['innerBlocks'])->count(3);
expect($result['innerBlocks'][0]['email_attrs']['width'])->equals(185); // (600 - 20 - 20) * 0.33
expect($result['innerBlocks'][1]['email_attrs']['width'])->equals(100);
expect($result['innerBlocks'][2]['email_attrs']['width'])->equals(112); // (600 - 20 - 20) * 0.2
}
public function testItCalculatesWidthOfBlockInColumn(): void {
$blocks = [[
'blockName' => 'core/columns',
'attrs' => [],
'innerBlocks' => [
[
'blockName' => 'core/column',
'attrs' => [
'width' => '40%',
'style' => [
'spacing' => [
'padding' => [
'left' => '10px',
'right' => '10px',
],
],
],
],
'innerBlocks' => [
[
'blockName' => 'core/paragraph',
'attrs' => [],
'innerBlocks' => [],
],
],
],
[
'blockName' => 'core/column',
'attrs' => [
'width' => '60%',
'style' => [
'spacing' => [
'padding' => [
'left' => '25px',
'right' => '15px',
],
],
],
],
'innerBlocks' => [
[
'blockName' => 'core/paragraph',
'attrs' => [],
'innerBlocks' => [],
],
],
],
],
]];
$result = $this->preprocessor->preprocess($blocks, ['width' => 660, 'padding' => ['left' => 15, 'right' => 15]]);
$innerBlocks = $result[0]['innerBlocks'];
expect($innerBlocks)->count(2);
expect($innerBlocks[0]['email_attrs']['width'])->equals(252); // (660 - 15 - 15) * 0.4
expect($innerBlocks[0]['innerBlocks'][0]['email_attrs']['width'])->equals(232); // paragraph: 252 - 10 - 10
expect($innerBlocks[1]['email_attrs']['width'])->equals(378); // (660 - 15 - 15) * 0.6
expect($innerBlocks[1]['innerBlocks'][0]['email_attrs']['width'])->equals(338); // paragraph: 378 - 25 - 15
}
public function testItAddsMissingColumnWidth(): void {
$blocks = [[
'blockName' => 'core/columns',
'attrs' => [],
'innerBlocks' => [
[
'blockName' => 'core/column',
'attrs' => [],
'innerBlocks' => [
[
'blockName' => 'core/paragraph',
'attrs' => [],
'innerBlocks' => [],
],
],
],
[
'blockName' => 'core/column',
'attrs' => [],
'innerBlocks' => [
[
'blockName' => 'core/paragraph',
'attrs' => [],
'innerBlocks' => [],
],
],
],
[
'blockName' => 'core/column',
'attrs' => [],
'innerBlocks' => [
[
'blockName' => 'core/paragraph',
'attrs' => [],
'innerBlocks' => [],
],
],
],
],
]];
$result = $this->preprocessor->preprocess($blocks, ['width' => 660, 'padding' => ['left' => 30, 'right' => 30]]);
$innerBlocks = $result[0]['innerBlocks'];
expect($innerBlocks)->count(3);
expect($innerBlocks[0]['email_attrs']['width'])->equals(200); // (660 - 30 - 30) * 0.33
expect($innerBlocks[0]['innerBlocks'][0]['email_attrs']['width'])->equals(200); // paragraph: 200
expect($innerBlocks[1]['email_attrs']['width'])->equals(200); // (660 - 30 - 30) * 0.33
expect($innerBlocks[1]['innerBlocks'][0]['email_attrs']['width'])->equals(200); // paragraph: 200
expect($innerBlocks[2]['email_attrs']['width'])->equals(200); // (660 - 30 - 30) * 0.33
expect($innerBlocks[2]['innerBlocks'][0]['email_attrs']['width'])->equals(200); // paragraph: 200
}
}