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
66 changes: 66 additions & 0 deletions src/Elements/BodyComponents/MjRaw.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?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 Raw Element
*
* Displays raw HTML that is not going to be parsed by the MJML engine.
* Anything left inside this tag should be raw, responsive HTML.
*
* @link https://documentation.mjml.io/#mj-raw
*
* @since 1.0.0
*/
class MjRaw extends AbstractElement
{
public const string TAG_NAME = 'mj-raw';

public const bool ENDING_TAG = true;

/**
* List of allowed attributes on the element
*
* mj-raw has no attributes - it only passes through content
*
* @var array<string, array<string, string>>
*/
protected array $allowedAttributes = [];

protected array $defaultAttributes = [];

/**
* Override getContent to preserve raw content without trimming
*/
protected function getContent(): string
{
return $this->content;
}

public function render(): string
{
// Return content as-is without any processing
return $this->getContent();
}

/**
* @return array<string, array<string, string>>
*/
public function getStyles(): array
{
// No styles needed for raw HTML passthrough
return [];
}
}
162 changes: 162 additions & 0 deletions tests/Unit/Elements/BodyComponents/MjRawTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php

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

use MadeByDenis\PhpMjmlRenderer\Elements\BodyComponents\MjRaw;
use MadeByDenis\PhpMjmlRenderer\Elements\ElementFactory;
use MadeByDenis\PhpMjmlRenderer\Parser\MjmlNode;

beforeEach(function () {
$this->element = new MjRaw();
});

it('is ending tag', function () {
expect($this->element->isEndingTag())->toBe(true);
});

it('returns the correct component name', function () {
expect($this->element->getTagName())->toBe('mj-raw');
});

it('has no default attributes', function () {
// mj-raw has no attributes, so we just verify it doesn't error
expect($this->element)->toBeInstanceOf(MjRaw::class);
});

it('will correctly render raw HTML', function () {
$rawNode = new MjmlNode(
'mj-raw',
null,
'<div style="color: red;">Raw HTML Content</div>',
true,
null
);

$factory = new ElementFactory();
$mjRawElement = $factory->create($rawNode);

expect($mjRawElement)->toBeInstanceOf(MjRaw::class);

$out = $mjRawElement->render();

expect($out)->toBe('<div style="color: red;">Raw HTML Content</div>');
expect($out)->not->toBeEmpty();
});

it('will pass through complex HTML unchanged', function () {
$complexHtml = '<table><tr><td style="padding: 10px;"><a href="https://example.com">Link</a></td></tr></table>';

$rawNode = new MjmlNode(
'mj-raw',
null,
$complexHtml,
true,
null
);

$factory = new ElementFactory();
$mjRawElement = $factory->create($rawNode);

$out = $mjRawElement->render();

expect($out)->toBe($complexHtml);
});

it('will preserve whitespace and formatting', function () {
$htmlWithWhitespace = " <div>\n <p>Paragraph</p>\n </div> ";

$rawNode = new MjmlNode(
'mj-raw',
null,
$htmlWithWhitespace,
true,
null
);

$factory = new ElementFactory();
$mjRawElement = $factory->create($rawNode);

$out = $mjRawElement->render();

expect($out)->toBe($htmlWithWhitespace);
});

it('will pass through empty content', function () {
$rawNode = new MjmlNode(
'mj-raw',
null,
'',
true,
null
);

$factory = new ElementFactory();
$mjRawElement = $factory->create($rawNode);

$out = $mjRawElement->render();

expect($out)->toBe('');
});

it('will pass through HTML comments', function () {
$htmlWithComments = '<!-- This is a comment --><div>Content</div><!-- Another comment -->';

$rawNode = new MjmlNode(
'mj-raw',
null,
$htmlWithComments,
true,
null
);

$factory = new ElementFactory();
$mjRawElement = $factory->create($rawNode);

$out = $mjRawElement->render();

expect($out)->toBe($htmlWithComments);
});

it('will pass through scripts and styles', function () {
$htmlWithScript = '<script>console.log("test");</script><style>.class { color: blue; }</style>';

$rawNode = new MjmlNode(
'mj-raw',
null,
$htmlWithScript,
true,
null
);

$factory = new ElementFactory();
$mjRawElement = $factory->create($rawNode);

$out = $mjRawElement->render();

expect($out)->toBe($htmlWithScript);
});

it('will pass through special characters', function () {
$htmlWithSpecialChars = '<div>&nbsp;&copy;&trade;<>&"\'</div>';

$rawNode = new MjmlNode(
'mj-raw',
null,
$htmlWithSpecialChars,
true,
null
);

$factory = new ElementFactory();
$mjRawElement = $factory->create($rawNode);

$out = $mjRawElement->render();

expect($out)->toBe($htmlWithSpecialChars);
});

it('returns empty styles array', function () {
$styles = $this->element->getStyles();
expect($styles)->toBeArray();
expect($styles)->toBeEmpty();
});