Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions src/Elements/BodyComponents/MjCarousel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

/**
* PHP MJML Renderer library
*
* @package MadeByDenis\PhpMjmlRenderer
* @link https://github.com/dingo-d/php-mjml-renderer
* @license https://opensource.org/licenses/MIT MIT
*/

declare(strict_types=1);

namespace MadeByDenis\PhpMjmlRenderer\Elements\BodyComponents;

use MadeByDenis\PhpMjmlRenderer\Elements\AbstractElement;

/**
* Mjml Carousel Element
*
* Displays an interactive image carousel with navigation.
*
* @link https://documentation.mjml.io/#mj-carousel
*
* @since 1.0.0
*/
class MjCarousel extends AbstractElement
{
public const string TAG_NAME = 'mj-carousel';

public const bool ENDING_TAG = false;

/**
* List of allowed attributes on the element
*
* @var array<string, array<string, string>>
*/
protected array $allowedAttributes = [
'align' => [
'unit' => 'string',
'type' => 'alignment',
'description' => 'horizontal alignment',
'default_value' => 'center',
],
'border-radius' => [
'unit' => 'px',
'type' => 'string',
'description' => 'border radius',
'default_value' => '',
],
'container-background-color' => [
'unit' => 'color',
'type' => 'color',
'description' => 'column background color',
'default_value' => '',
],
'icon-width' => [
'unit' => 'px',
'type' => 'string',
'description' => 'width of icons',
'default_value' => '44px',
],
'left-icon' => [
'unit' => 'string',
'type' => 'string',
'description' => 'icon on the left',
'default_value' => 'https://i.imgur.com/xTh3hln.png',
],
'right-icon' => [
'unit' => 'string',
'type' => 'string',
'description' => 'icon on the right',
'default_value' => 'https://i.imgur.com/os7o9kz.png',
],
'thumbnails' => [
'unit' => 'string',
'type' => 'string',
'description' => 'display or not thumbnails (visible/hidden)',
'default_value' => 'visible',
],
];

protected array $defaultAttributes = [
'align' => 'center',
'icon-width' => '44px',
'left-icon' => 'https://i.imgur.com/xTh3hln.png',
'right-icon' => 'https://i.imgur.com/os7o9kz.png',
'thumbnails' => 'visible',
];

public function render(): string
{
$divAttributes = $this->getHtmlAttributes([
'style' => 'div',
]);

$children = $this->getChildren() ?? [];
$content = $this->renderChildren($children, []);

return "<div $divAttributes>$content</div>";
}

/**
* @return array<string, array<string, string>>
*/
public function getStyles(): array
{
return [
'div' => [
'text-align' => $this->getAttribute('align'),
'border-radius' => $this->getAttribute('border-radius'),
],
];
}
}
109 changes: 109 additions & 0 deletions src/Elements/BodyComponents/MjCarouselImage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

/**
* PHP MJML Renderer library
*
* @package MadeByDenis\PhpMjmlRenderer
* @link https://github.com/dingo-d/php-mjml-renderer
* @license https://opensource.org/licenses/MIT MIT
*/

declare(strict_types=1);

namespace MadeByDenis\PhpMjmlRenderer\Elements\BodyComponents;

use MadeByDenis\PhpMjmlRenderer\Elements\AbstractElement;

/**
* Mjml Carousel Image Element
*
* Individual image item in a carousel.
*
* @link https://documentation.mjml.io/#mj-carousel-image
*
* @since 1.0.0
*/
class MjCarouselImage extends AbstractElement
{
public const string TAG_NAME = 'mj-carousel-image';

public const bool ENDING_TAG = false;

/**
* List of allowed attributes on the element
*
* @var array<string, array<string, string>>
*/
protected array $allowedAttributes = [
'alt' => [
'unit' => 'string',
'type' => 'string',
'description' => 'image description',
'default_value' => '',
],
'href' => [
'unit' => 'string',
'type' => 'string',
'description' => 'link to redirect to on click',
'default_value' => '',
],
'rel' => [
'unit' => 'string',
'type' => 'string',
'description' => 'specify the rel attribute',
'default_value' => '',
],
'src' => [
'unit' => 'string',
'type' => 'string',
'description' => 'image source',
'default_value' => '',
],
'target' => [
'unit' => 'string',
'type' => 'string',
'description' => 'link target on click',
'default_value' => '_blank',
],
'thumbnails-src' => [
'unit' => 'string',
'type' => 'string',
'description' => 'thumbnail source',
'default_value' => '',
],
'title' => [
'unit' => 'string',
'type' => 'string',
'description' => 'image title',
'default_value' => '',
],
];

protected array $defaultAttributes = [
'target' => '_blank',
];

public function render(): string
{
$src = $this->getAttribute('src');
$alt = $this->getAttribute('alt');
$href = $this->getAttribute('href');
$target = $this->getAttribute('target');

$imgTag = "<img src='$src' alt='$alt' style='max-width: 100%;' />";

if ($href) {
return "<a href='$href' target='$target'>$imgTag</a>";
}

return $imgTag;
}

/**
* @return array<string, array<string, string>>
*/
public function getStyles(): array
{
return [];
}
}
50 changes: 50 additions & 0 deletions tests/Unit/Elements/BodyComponents/MjCarouselTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace MadeByDenis\PhpMjmlRenderer\Tests\Unit\Elements\BodyComponents;

use MadeByDenis\PhpMjmlRenderer\Elements\BodyComponents\MjCarousel;
use MadeByDenis\PhpMjmlRenderer\Elements\BodyComponents\MjCarouselImage;

describe('MjCarousel', function () {
it('returns the correct component name', function () {
$element = new MjCarousel();
expect($element->getTagName())->toBe('mj-carousel');
});

it('has correct default attributes', function () {
$element = new MjCarousel();
expect($element->getAttribute('align'))->toBe('center');
expect($element->getAttribute('thumbnails'))->toBe('visible');
});

it('renders correctly', function () {
$element = new MjCarousel();
$out = $element->render();
expect($out)->toContain('<div');
});
});

describe('MjCarouselImage', function () {
it('returns the correct component name', function () {
$element = new MjCarouselImage();
expect($element->getTagName())->toBe('mj-carousel-image');
});

it('renders image correctly', function () {
$element = new MjCarouselImage(['src' => 'test.jpg', 'alt' => 'Test']);
$out = $element->render();
expect($out)->toContain('<img');
expect($out)->toContain('test.jpg');
expect($out)->toContain('Test');
});

it('renders with link when href is provided', function () {
$element = new MjCarouselImage([
'src' => 'test.jpg',
'href' => 'https://example.com',
]);
$out = $element->render();
expect($out)->toContain('<a href=');
expect($out)->toContain('https://example.com');
});
});