From 7a604fa10e4f76346c0f85e2d3db6721f6278ecf Mon Sep 17 00:00:00 2001 From: Rostislav Wolny Date: Thu, 2 Apr 2020 10:53:27 +0200 Subject: [PATCH] Render image block on front end [MAILPOET-2750] --- assets/css/src/components-public/_public.scss | 27 +++++++ lib/DI/ContainerConfigurator.php | 1 + lib/Form/Block/Image.php | 48 ++++++++++++ lib/Form/BlocksRenderer.php | 10 +++ tests/unit/Form/Block/ImageTest.php | 78 +++++++++++++++++++ tests/unit/Form/HtmlParser.php | 13 +++- 6 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 lib/Form/Block/Image.php create mode 100644 tests/unit/Form/Block/ImageTest.php diff --git a/assets/css/src/components-public/_public.scss b/assets/css/src/components-public/_public.scss index 0482e207e8..cc8bec82b6 100644 --- a/assets/css/src/components-public/_public.scss +++ b/assets/css/src/components-public/_public.scss @@ -206,3 +206,30 @@ div.mailpoet_form_popup { div.mailpoet_form_popup.active { display: block; } + +// MailPoet Form - Image block +// Inspired by wp-image-block behaviour +.mailpoet_form_image { + &.is-style-rounded { + border-radius: 9999px; + } + + .aligncenter { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + } + .alignleft { + float: left; + margin-right: 1em; + } + .alignright { + float: right; + margin-left: 1em; + } + + figcaption { + text-align: center; + } +} diff --git a/lib/DI/ContainerConfigurator.php b/lib/DI/ContainerConfigurator.php index 58167589d8..4c9fbbd05c 100644 --- a/lib/DI/ContainerConfigurator.php +++ b/lib/DI/ContainerConfigurator.php @@ -185,6 +185,7 @@ class ContainerConfigurator implements IContainerConfigurator { $container->autowire(\MailPoet\Form\Block\Divider::class); $container->autowire(\MailPoet\Form\Block\Html::class); $container->autowire(\MailPoet\Form\Block\Heading::class); + $container->autowire(\MailPoet\Form\Block\Image::class); $container->autowire(\MailPoet\Form\Block\Paragraph::class); $container->autowire(\MailPoet\Form\Block\Radio::class); $container->autowire(\MailPoet\Form\Block\Segment::class); diff --git a/lib/Form/Block/Image.php b/lib/Form/Block/Image.php new file mode 100644 index 0000000000..e48801b6a0 --- /dev/null +++ b/lib/Form/Block/Image.php @@ -0,0 +1,48 @@ +wrapImage($block['params'], $this->renderImage($block['params'])); + } + + private function renderImage(array $params) { + $attributes = []; + $attributes[] = 'src="' . $params['url'] . '"'; + $attributes[] = $params['alt'] ? 'alt="' . $params['alt'] . '"' : 'alt'; + if ($params['title']) { + $attributes[] = 'title="' . $params['title'] . '"'; + } + if ($params['id']) { + // WordPress automatically renders srcset based on this class + $attributes[] = 'class="wp-image-' . $params['id'] . '"'; + } + if ($params['width']) { + $attributes[] = 'width="' . intval($params['width']) . '"'; + } + if ($params['height']) { + $attributes[] = 'height="' . intval($params['height']) . '"'; + } + return ''; + } + + private function wrapImage(array $params, string $img) { + // Figure + $figureClasses = ['size-' . $params['size_slug']]; + if ($params['align']) { + $figureClasses[] = 'align' . $params['align']; + } + $caption = $params['caption'] ? "
{$params['caption']}
" : ''; + $figure = '
' . $img . $caption . '
'; + // Main wrapper + $divClasses = ['mailpoet_form_image']; + if (trim($params['class_name'])) { + $divClasses[] = trim($params['class_name']); + } + return '
' . $figure . '
'; + } +} diff --git a/lib/Form/BlocksRenderer.php b/lib/Form/BlocksRenderer.php index 713f897e50..d27dd0e7a7 100644 --- a/lib/Form/BlocksRenderer.php +++ b/lib/Form/BlocksRenderer.php @@ -9,6 +9,7 @@ use MailPoet\Form\Block\Date; use MailPoet\Form\Block\Divider; use MailPoet\Form\Block\Heading; use MailPoet\Form\Block\Html; +use MailPoet\Form\Block\Image; use MailPoet\Form\Block\Paragraph; use MailPoet\Form\Block\Radio; use MailPoet\Form\Block\Segment; @@ -30,6 +31,9 @@ class BlocksRenderer { /** @var Html */ private $html; + /** @var Image */ + private $image; + /** @var Radio */ private $radio; @@ -67,6 +71,7 @@ class BlocksRenderer { Date $date, Divider $divider, Html $html, + Image $image, Heading $heading, Paragraph $paragraph, Radio $radio, @@ -82,6 +87,7 @@ class BlocksRenderer { $this->date = $date; $this->divider = $divider; $this->html = $html; + $this->image = $image; $this->radio = $radio; $this->segment = $segment; $this->select = $select; @@ -103,6 +109,10 @@ class BlocksRenderer { $html .= $this->heading->render($block); break; + case 'image': + $html .= $this->image->render($block); + break; + case 'paragraph': $html .= $this->paragraph->render($block); break; diff --git a/tests/unit/Form/Block/ImageTest.php b/tests/unit/Form/Block/ImageTest.php new file mode 100644 index 0000000000..4ec1346932 --- /dev/null +++ b/tests/unit/Form/Block/ImageTest.php @@ -0,0 +1,78 @@ + 'image', + 'id' => 'image', + 'params' => [ + 'class_name' => 'my-class', + 'align' => 'left', + 'url' => 'http://example.com/image.jpg', + 'alt' => 'Alt text', + 'title' => 'Title', + 'caption' => 'Caption', + 'link_destination' => 'none', + 'link' => null, + 'id' => 123, + 'size_slug' => 'medium', + 'width' => 100, + 'height' => 200, + ], + ]; + + /** @var HtmlParser */ + private $htmlParser; + + public function _before() { + parent::_before(); + $this->image = new Image(); + $this->htmlParser = new HtmlParser(); + } + + public function testItShouldRenderImageBlock() { + $html = $this->image->render($this->block); + $block = $this->htmlParser->getElementByXpath($html, '//div'); + $blockClass = $this->htmlParser->getAttribute($block, 'class'); + expect($blockClass->value)->equals('mailpoet_form_image my-class'); + + $figure = $this->htmlParser->getChildElement($block, 'figure'); + $figureClass = $this->htmlParser->getAttribute($figure, 'class'); + expect($figureClass->value)->equals('size-medium alignleft'); + + $img = $this->htmlParser->getChildElement($figure, 'img'); + $imgSrc = $this->htmlParser->getAttribute($img, 'src'); + expect($imgSrc->value)->equals('http://example.com/image.jpg'); + $imgWidth = $this->htmlParser->getAttribute($img, 'width'); + expect($imgWidth->value)->equals(100); + $imgHeight = $this->htmlParser->getAttribute($img, 'height'); + expect($imgHeight->value)->equals(200); + $imgTitle = $this->htmlParser->getAttribute($img, 'title'); + expect($imgTitle->value)->equals('Title'); + $imgAlt = $this->htmlParser->getAttribute($img, 'alt'); + expect($imgAlt->value)->equals('Alt text'); + + $caption = $this->htmlParser->getChildElement($figure, 'figcaption'); + expect($caption->textContent)->equals('Caption'); + } + + public function testItRendersNothingWhenUrlIsEmpty() { + $block = $this->block; + $block['params']['url'] = null; + $html = $this->image->render($block); + expect($html)->equals(''); + $block['params']['url'] = ''; + $html = $this->image->render($block); + expect($html)->equals(''); + } +} diff --git a/tests/unit/Form/HtmlParser.php b/tests/unit/Form/HtmlParser.php index 3021d4fd73..415faeba60 100644 --- a/tests/unit/Form/HtmlParser.php +++ b/tests/unit/Form/HtmlParser.php @@ -3,9 +3,20 @@ namespace MailPoet\Test\Form; class HtmlParser { + + private $allowedHtml5Tags = ['allowedHtml5Tags, '', $html) !== $html; $dom = new \DOMDocument(); - $dom->loadHTML($html); + if ($isHtml5) { + // HTML 5 tags like figure, nav are not supported so we need to turn off errors + libxml_use_internal_errors(true); + $dom->loadHTML($html); + libxml_clear_errors(); + } else { + $dom->loadHTML($html); + } $value = (new \DOMXPath($dom))->query($xpath); return $value ?: new \DOMNodeList(); }