Add border support to the image renderer
This commit fixes how the border is applied in an image block. - the border is applied to the wrapping table cell instead of the image - we need to move classes related to border styles to the cell so that related styles are inlined in the proper place - the caption is put in an additional table so that it doesn't extend the border when it is longer than the image - rounded image styles are now applied to the wrapper and the image [MAILPOET-6280]
This commit is contained in:
committed by
Pavel Dohnal
parent
10ad46239d
commit
9829c96caa
@@ -7,7 +7,7 @@ use MailPoet\EmailEditor\Integrations\Utils\DomDocumentHelper;
|
|||||||
|
|
||||||
class Image extends AbstractBlockRenderer {
|
class Image extends AbstractBlockRenderer {
|
||||||
protected function renderContent($blockContent, array $parsedBlock, SettingsController $settingsController): string {
|
protected function renderContent($blockContent, array $parsedBlock, SettingsController $settingsController): string {
|
||||||
$parsedHtml = $this->parseBlockContent($this->cleanupContent($blockContent));
|
$parsedHtml = $this->parseBlockContent($blockContent);
|
||||||
|
|
||||||
if (!$parsedHtml) {
|
if (!$parsedHtml) {
|
||||||
return '';
|
return '';
|
||||||
@@ -16,28 +16,31 @@ class Image extends AbstractBlockRenderer {
|
|||||||
$imageUrl = $parsedHtml['imageUrl'];
|
$imageUrl = $parsedHtml['imageUrl'];
|
||||||
$image = $parsedHtml['image'];
|
$image = $parsedHtml['image'];
|
||||||
$caption = $parsedHtml['caption'];
|
$caption = $parsedHtml['caption'];
|
||||||
|
$class = $parsedHtml['class'];
|
||||||
$parsedBlock = $this->addImageSizeWhenMissing($parsedBlock, $imageUrl, $settingsController);
|
$parsedBlock = $this->addImageSizeWhenMissing($parsedBlock, $imageUrl, $settingsController);
|
||||||
$image = $this->addImageDimensions($image, $parsedBlock, $settingsController);
|
$image = $this->addImageDimensions($image, $parsedBlock, $settingsController);
|
||||||
$image = $this->applyImageBorderStyle($image, $parsedBlock, $settingsController);
|
|
||||||
$image = $this->applyRoundedStyle($image, $parsedBlock);
|
|
||||||
|
|
||||||
return str_replace(
|
$imageWithWrapper = str_replace(
|
||||||
['{image_content}', '{caption_content}'],
|
['{image_content}', '{caption_content}'],
|
||||||
[$image, $caption],
|
[$image, $caption],
|
||||||
$this->getBlockWrapper($parsedBlock, $settingsController)
|
$this->getBlockWrapper($parsedBlock, $settingsController)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$imageWithWrapper = $this->applyRoundedStyle($imageWithWrapper, $parsedBlock);
|
||||||
|
$imageWithWrapper = $this->applyImageBorderStyle($imageWithWrapper, $parsedBlock, $class);
|
||||||
|
return $imageWithWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applyRoundedStyle(string $blockContent, array $parsedBlock): string {
|
private function applyRoundedStyle(string $blockContent, array $parsedBlock): string {
|
||||||
// Because the isn't an attribute for definition of rounded style, we have to check the class name
|
// Because the isn't an attribute for definition of rounded style, we have to check the class name
|
||||||
if (isset($parsedBlock['attrs']['className']) && strpos($parsedBlock['attrs']['className'], 'is-style-rounded') !== false) {
|
if (isset($parsedBlock['attrs']['className']) && strpos($parsedBlock['attrs']['className'], 'is-style-rounded') !== false) {
|
||||||
// If the image should be in a circle, we need to set the border-radius to 9999px to make it the same as is in the editor
|
// If the image should be in a circle, we need to set the border-radius to 9999px to make it the same as is in the editor
|
||||||
// This style cannot be applied on the wrapper, and we need to set it directly on the image
|
// This style is applied to both wrapper and the image
|
||||||
|
$blockContent = $this->removeStyleAttributeFromElement($blockContent, ['tag_name' => 'td', 'class_name' => 'email-image-cell'], 'border-radius');
|
||||||
|
$blockContent = $this->addStyleToElement($blockContent, ['tag_name' => 'td', 'class_name' => 'email-image-cell'], 'border-radius: 9999px;');
|
||||||
$blockContent = $this->removeStyleAttributeFromElement($blockContent, ['tag_name' => 'img'], 'border-radius');
|
$blockContent = $this->removeStyleAttributeFromElement($blockContent, ['tag_name' => 'img'], 'border-radius');
|
||||||
$blockContent = $this->addStyleToElement($blockContent, ['tag_name' => 'img'], 'border-radius: 9999px;');
|
$blockContent = $this->addStyleToElement($blockContent, ['tag_name' => 'img'], 'border-radius: 9999px;');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $blockContent;
|
return $blockContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,19 +58,14 @@ class Image extends AbstractBlockRenderer {
|
|||||||
$maxWidth = $settingsController->parseNumberFromStringWithPixels($parsedBlock['email_attrs']['width']);
|
$maxWidth = $settingsController->parseNumberFromStringWithPixels($parsedBlock['email_attrs']['width']);
|
||||||
$imageSize = wp_getimagesize($imageUrl);
|
$imageSize = wp_getimagesize($imageUrl);
|
||||||
$imageSize = $imageSize ? $imageSize[0] : $maxWidth;
|
$imageSize = $imageSize ? $imageSize[0] : $maxWidth;
|
||||||
// Because width is primarily used for the max-width property, we need to add the left and right border width to it
|
|
||||||
$borderWidth = $parsedBlock['attrs']['style']['border']['width'] ?? '0px';
|
|
||||||
$borderLeftWidth = $parsedBlock['attrs']['style']['border']['left']['width'] ?? $borderWidth;
|
|
||||||
$borderRightWidth = $parsedBlock['attrs']['style']['border']['right']['width'] ?? $borderWidth;
|
|
||||||
$width = min($imageSize, $maxWidth);
|
$width = min($imageSize, $maxWidth);
|
||||||
$width += $settingsController->parseNumberFromStringWithPixels($borderLeftWidth ?? '0px');
|
|
||||||
$width += $settingsController->parseNumberFromStringWithPixels($borderRightWidth ?? '0px');
|
|
||||||
$parsedBlock['attrs']['width'] = "{$width}px";
|
$parsedBlock['attrs']['width'] = "{$width}px";
|
||||||
return $parsedBlock;
|
return $parsedBlock;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applyImageBorderStyle(string $blockContent, array $parsedBlock, SettingsController $settingsController): string {
|
private function applyImageBorderStyle(string $blockContent, array $parsedBlock, string $class): string {
|
||||||
|
|
||||||
// Getting individual border properties
|
// Getting individual border properties
|
||||||
$borderStyles = wp_style_engine_get_styles(['border' => $parsedBlock['attrs']['style']['border'] ?? []]);
|
$borderStyles = wp_style_engine_get_styles(['border' => $parsedBlock['attrs']['style']['border'] ?? []]);
|
||||||
$borderStyles = $borderStyles['declarations'] ?? [];
|
$borderStyles = $borderStyles['declarations'] ?? [];
|
||||||
@@ -75,8 +73,19 @@ class Image extends AbstractBlockRenderer {
|
|||||||
$borderStyles['border-style'] = 'solid';
|
$borderStyles['border-style'] = 'solid';
|
||||||
$borderStyles['box-sizing'] = 'border-box';
|
$borderStyles['box-sizing'] = 'border-box';
|
||||||
}
|
}
|
||||||
|
$borderElementTag = ['tag_name' => 'td', 'class_name' => 'email-image-cell'];
|
||||||
return $this->addStyleToElement($blockContent, ['tag_name' => 'img'], \WP_Style_Engine::compile_css($borderStyles, ''));
|
$contentWithBorderStyles = $this->addStyleToElement($blockContent, $borderElementTag, \WP_Style_Engine::compile_css($borderStyles, ''));
|
||||||
|
// Add Border related classes to proper element. This is required for inlined border-color styles when defined via class
|
||||||
|
$borderClasses = array_filter(explode(' ', $class), function($className) {
|
||||||
|
return strpos($className, 'border') !== false;
|
||||||
|
});
|
||||||
|
$html = new \WP_HTML_Tag_Processor($contentWithBorderStyles);
|
||||||
|
if ($html->next_tag($borderElementTag)) {
|
||||||
|
$class = $html->get_attribute('class') ?? '';
|
||||||
|
$borderClasses[] = $class;
|
||||||
|
$html->set_attribute('class', implode(' ', $borderClasses));
|
||||||
|
}
|
||||||
|
return $html->get_updated_html();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,7 +121,7 @@ class Image extends AbstractBlockRenderer {
|
|||||||
$themeData = $settingsController->getTheme()->get_data();
|
$themeData = $settingsController->getTheme()->get_data();
|
||||||
|
|
||||||
$styles = [
|
$styles = [
|
||||||
'text-align' => 'center',
|
'text-align' => isset($parsedBlock['attrs']['align']) ? 'center' : 'left',
|
||||||
];
|
];
|
||||||
|
|
||||||
$styles['font-size'] = $parsedBlock['email_attrs']['font-size'] ?? $themeData['styles']['typography']['fontSize'];
|
$styles['font-size'] = $parsedBlock['email_attrs']['font-size'] ?? $themeData['styles']['typography']['fontSize'];
|
||||||
@@ -131,11 +140,16 @@ class Image extends AbstractBlockRenderer {
|
|||||||
'width' => '100%',
|
'width' => '100%',
|
||||||
];
|
];
|
||||||
|
|
||||||
// When the image is not aligned, the wrapper is set to 100% width due to caption that can be longer than the image
|
$width = $parsedBlock['attrs']['width'] ?? '100%';
|
||||||
$wrapperWidth = isset($parsedBlock['attrs']['align']) ? ($parsedBlock['attrs']['width'] ?? '100%') : '100%';
|
$wrapperWidth = ($width && $width !== '100%') ? $width : 'auto';
|
||||||
$wrapperStyles = $styles;
|
$wrapperStyles = $styles;
|
||||||
$wrapperStyles['width'] = $wrapperWidth;
|
$wrapperStyles['width'] = $wrapperWidth;
|
||||||
|
$wrapperStyles['border-collapse'] = 'separate'; // Needed because of border radius
|
||||||
|
|
||||||
|
// When the image is not aligned, the wrapper is set to 100% width due to caption that can be longer than the image
|
||||||
|
$captionWidth = isset($parsedBlock['attrs']['align']) ? ($parsedBlock['attrs']['width'] ?? '100%') : '100%';
|
||||||
|
$captionWrapperStyles = $styles;
|
||||||
|
$captionWrapperStyles['width'] = $captionWidth;
|
||||||
$captionStyles = $this->getCaptionStyles($settingsController, $parsedBlock);
|
$captionStyles = $this->getCaptionStyles($settingsController, $parsedBlock);
|
||||||
|
|
||||||
$styles['width'] = '100%';
|
$styles['width'] = '100%';
|
||||||
@@ -162,8 +176,18 @@ class Image extends AbstractBlockRenderer {
|
|||||||
width="' . esc_attr($wrapperWidth) . '"
|
width="' . esc_attr($wrapperWidth) . '"
|
||||||
>
|
>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{image_content}</td>
|
<td class="email-image-cell">{image_content}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table
|
||||||
|
role="presentation"
|
||||||
|
class="email-table-with-width"
|
||||||
|
border="0"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
style="' . esc_attr(\WP_Style_Engine::compile_css($captionWrapperStyles, '')) . '"
|
||||||
|
width="' . esc_attr($captionWidth) . '"
|
||||||
|
>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="' . esc_attr($captionStyles) . '">{caption_content}</td>
|
<td style="' . esc_attr($captionStyles) . '">{caption_content}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -229,21 +253,21 @@ class Image extends AbstractBlockRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$imageSrc = $domHelper->getAttributeValue($imgTag, 'src');
|
$imageSrc = $domHelper->getAttributeValue($imgTag, 'src');
|
||||||
|
$imageClass = $domHelper->getAttributeValue($imgTag, 'class');
|
||||||
$imageHtml = $domHelper->getOuterHtml($imgTag);
|
$imageHtml = $domHelper->getOuterHtml($imgTag);
|
||||||
|
|
||||||
$figcaption = $domHelper->findElement('figcaption');
|
$figcaption = $domHelper->findElement('figcaption');
|
||||||
$figcaptionHtml = $figcaption ? $domHelper->getOuterHtml($figcaption) : '';
|
$figcaptionHtml = $figcaption ? $domHelper->getOuterHtml($figcaption) : '';
|
||||||
$figcaptionHtml = str_replace(['<figcaption', '</figcaption>'], ['<span', '</span>'], $figcaptionHtml);
|
$figcaptionHtml = str_replace(['<figcaption', '</figcaption>'], ['<span', '</span>'], $figcaptionHtml);
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'imageUrl' => $imageSrc ?: '',
|
'imageUrl' => $imageSrc ?: '',
|
||||||
'image' => $imageHtml,
|
'image' => $this->cleanupImageHtml($imageHtml),
|
||||||
'caption' => $figcaptionHtml ?: '',
|
'caption' => $figcaptionHtml ?: '',
|
||||||
|
'class' => $imageClass ?: '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanupContent(string $contentHtml): string {
|
private function cleanupImageHtml(string $contentHtml): string {
|
||||||
$html = new \WP_HTML_Tag_Processor($contentHtml);
|
$html = new \WP_HTML_Tag_Processor($contentHtml);
|
||||||
if ($html->next_tag(['tag_name' => 'img'])) {
|
if ($html->next_tag(['tag_name' => 'img'])) {
|
||||||
$html->remove_attribute('srcset');
|
$html->remove_attribute('srcset');
|
||||||
|
@@ -96,4 +96,45 @@ class ImageTest extends \MailPoetTest {
|
|||||||
$this->assertStringContainsString('height:300px;', $rendered);
|
$this->assertStringContainsString('height:300px;', $rendered);
|
||||||
$this->assertStringContainsString('width:400px;', $rendered);
|
$this->assertStringContainsString('width:400px;', $rendered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testItRendersBorders(): void {
|
||||||
|
$imageContent = $this->imageContent;
|
||||||
|
$parsedImage = $this->parsedImage;
|
||||||
|
$parsedImage['attrs']['style']['border'] = [
|
||||||
|
'width' => '10px',
|
||||||
|
'color' => '#000001',
|
||||||
|
'radius' => '20px',
|
||||||
|
];
|
||||||
|
|
||||||
|
$rendered = $this->imageRenderer->render($imageContent, $parsedImage, $this->settingsController);
|
||||||
|
$html = new \WP_HTML_Tag_Processor($rendered);
|
||||||
|
// Border is rendered on the wrapping table cell
|
||||||
|
$html->next_tag(['tag_name' => 'td', 'class_name' => 'email-image-cell']);
|
||||||
|
$tableCellStyle = $html->get_attribute('style');
|
||||||
|
$this->assertStringContainsString('border-color:#000001', $tableCellStyle);
|
||||||
|
$this->assertStringContainsString('border-radius:20px', $tableCellStyle);
|
||||||
|
$this->assertStringContainsString('border-style:solid;', $tableCellStyle);
|
||||||
|
$html->next_tag(['tag_name' => 'img']);
|
||||||
|
$imgStyle = $html->get_attribute('style');
|
||||||
|
$this->assertStringNotContainsString('border', $imgStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItMovesBorderRelatedClasses(): void {
|
||||||
|
$imageContent = str_replace('<img', '<img class="custom-class has-border-color has-border-red-color"',$this->imageContent);
|
||||||
|
$parsedImage = $this->parsedImage;
|
||||||
|
$parsedImage['attrs']['style']['border'] = [
|
||||||
|
'width' => '10px',
|
||||||
|
'color' => '#000001',
|
||||||
|
'radius' => '20px',
|
||||||
|
];
|
||||||
|
|
||||||
|
$rendered = $this->imageRenderer->render($imageContent, $parsedImage, $this->settingsController);
|
||||||
|
$html = new \WP_HTML_Tag_Processor($rendered);
|
||||||
|
// Border is rendered on the wrapping table cell and the border classes are moved to the wrapping table cell
|
||||||
|
$html->next_tag(['tag_name' => 'td', 'class_name' => 'email-image-cell']);
|
||||||
|
$tableCellClass = $html->get_attribute('class');
|
||||||
|
$this->assertStringContainsString('has-border-red-color', $tableCellClass);
|
||||||
|
$this->assertStringContainsString('has-border-color', $tableCellClass);
|
||||||
|
$this->assertStringNotContainsString('custom-class', $tableCellClass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user