Skip to content

Commit 6898e60

Browse files
committed
Implement mj-raw element
Add support for mj-raw element that passes through raw HTML without any MJML processing. Overrides getContent() to preserve whitespace and formatting exactly as provided.
1 parent 61e121c commit 6898e60

File tree

2 files changed

+228
-0
lines changed

2 files changed

+228
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
/**
4+
* PHP MJML Renderer library
5+
*
6+
* @package MadeByDenis\PhpMjmlRenderer
7+
* @link https://github.com/dingo-d/php-mjml-renderer
8+
* @license https://opensource.org/licenses/MIT MIT
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace MadeByDenis\PhpMjmlRenderer\Elements\BodyComponents;
14+
15+
use MadeByDenis\PhpMjmlRenderer\Elements\AbstractElement;
16+
17+
/**
18+
* Mjml Raw Element
19+
*
20+
* Displays raw HTML that is not going to be parsed by the MJML engine.
21+
* Anything left inside this tag should be raw, responsive HTML.
22+
*
23+
* @link https://documentation.mjml.io/#mj-raw
24+
*
25+
* @since 1.0.0
26+
*/
27+
class MjRaw extends AbstractElement
28+
{
29+
public const string TAG_NAME = 'mj-raw';
30+
31+
public const bool ENDING_TAG = true;
32+
33+
/**
34+
* List of allowed attributes on the element
35+
*
36+
* mj-raw has no attributes - it only passes through content
37+
*
38+
* @var array<string, array<string, string>>
39+
*/
40+
protected array $allowedAttributes = [];
41+
42+
protected array $defaultAttributes = [];
43+
44+
/**
45+
* Override getContent to preserve raw content without trimming
46+
*/
47+
protected function getContent(): string
48+
{
49+
return $this->content;
50+
}
51+
52+
public function render(): string
53+
{
54+
// Return content as-is without any processing
55+
return $this->getContent();
56+
}
57+
58+
/**
59+
* @return array<string, array<string, string>>
60+
*/
61+
public function getStyles(): array
62+
{
63+
// No styles needed for raw HTML passthrough
64+
return [];
65+
}
66+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<?php
2+
3+
namespace MadeByDenis\PhpMjmlRenderer\Tests\Unit\Elements\BodyComponents;
4+
5+
use MadeByDenis\PhpMjmlRenderer\Elements\BodyComponents\MjRaw;
6+
use MadeByDenis\PhpMjmlRenderer\Elements\ElementFactory;
7+
use MadeByDenis\PhpMjmlRenderer\Parser\MjmlNode;
8+
9+
beforeEach(function () {
10+
$this->element = new MjRaw();
11+
});
12+
13+
it('is ending tag', function () {
14+
expect($this->element->isEndingTag())->toBe(true);
15+
});
16+
17+
it('returns the correct component name', function () {
18+
expect($this->element->getTagName())->toBe('mj-raw');
19+
});
20+
21+
it('has no default attributes', function () {
22+
// mj-raw has no attributes, so we just verify it doesn't error
23+
expect($this->element)->toBeInstanceOf(MjRaw::class);
24+
});
25+
26+
it('will correctly render raw HTML', function () {
27+
$rawNode = new MjmlNode(
28+
'mj-raw',
29+
null,
30+
'<div style="color: red;">Raw HTML Content</div>',
31+
true,
32+
null
33+
);
34+
35+
$factory = new ElementFactory();
36+
$mjRawElement = $factory->create($rawNode);
37+
38+
expect($mjRawElement)->toBeInstanceOf(MjRaw::class);
39+
40+
$out = $mjRawElement->render();
41+
42+
expect($out)->toBe('<div style="color: red;">Raw HTML Content</div>');
43+
expect($out)->not->toBeEmpty();
44+
});
45+
46+
it('will pass through complex HTML unchanged', function () {
47+
$complexHtml = '<table><tr><td style="padding: 10px;"><a href="https://example.com">Link</a></td></tr></table>';
48+
49+
$rawNode = new MjmlNode(
50+
'mj-raw',
51+
null,
52+
$complexHtml,
53+
true,
54+
null
55+
);
56+
57+
$factory = new ElementFactory();
58+
$mjRawElement = $factory->create($rawNode);
59+
60+
$out = $mjRawElement->render();
61+
62+
expect($out)->toBe($complexHtml);
63+
});
64+
65+
it('will preserve whitespace and formatting', function () {
66+
$htmlWithWhitespace = " <div>\n <p>Paragraph</p>\n </div> ";
67+
68+
$rawNode = new MjmlNode(
69+
'mj-raw',
70+
null,
71+
$htmlWithWhitespace,
72+
true,
73+
null
74+
);
75+
76+
$factory = new ElementFactory();
77+
$mjRawElement = $factory->create($rawNode);
78+
79+
$out = $mjRawElement->render();
80+
81+
expect($out)->toBe($htmlWithWhitespace);
82+
});
83+
84+
it('will pass through empty content', function () {
85+
$rawNode = new MjmlNode(
86+
'mj-raw',
87+
null,
88+
'',
89+
true,
90+
null
91+
);
92+
93+
$factory = new ElementFactory();
94+
$mjRawElement = $factory->create($rawNode);
95+
96+
$out = $mjRawElement->render();
97+
98+
expect($out)->toBe('');
99+
});
100+
101+
it('will pass through HTML comments', function () {
102+
$htmlWithComments = '<!-- This is a comment --><div>Content</div><!-- Another comment -->';
103+
104+
$rawNode = new MjmlNode(
105+
'mj-raw',
106+
null,
107+
$htmlWithComments,
108+
true,
109+
null
110+
);
111+
112+
$factory = new ElementFactory();
113+
$mjRawElement = $factory->create($rawNode);
114+
115+
$out = $mjRawElement->render();
116+
117+
expect($out)->toBe($htmlWithComments);
118+
});
119+
120+
it('will pass through scripts and styles', function () {
121+
$htmlWithScript = '<script>console.log("test");</script><style>.class { color: blue; }</style>';
122+
123+
$rawNode = new MjmlNode(
124+
'mj-raw',
125+
null,
126+
$htmlWithScript,
127+
true,
128+
null
129+
);
130+
131+
$factory = new ElementFactory();
132+
$mjRawElement = $factory->create($rawNode);
133+
134+
$out = $mjRawElement->render();
135+
136+
expect($out)->toBe($htmlWithScript);
137+
});
138+
139+
it('will pass through special characters', function () {
140+
$htmlWithSpecialChars = '<div>&nbsp;&copy;&trade;<>&"\'</div>';
141+
142+
$rawNode = new MjmlNode(
143+
'mj-raw',
144+
null,
145+
$htmlWithSpecialChars,
146+
true,
147+
null
148+
);
149+
150+
$factory = new ElementFactory();
151+
$mjRawElement = $factory->create($rawNode);
152+
153+
$out = $mjRawElement->render();
154+
155+
expect($out)->toBe($htmlWithSpecialChars);
156+
});
157+
158+
it('returns empty styles array', function () {
159+
$styles = $this->element->getStyles();
160+
expect($styles)->toBeArray();
161+
expect($styles)->toBeEmpty();
162+
});

0 commit comments

Comments
 (0)