Skip to content

Commit 5a05f6d

Browse files
erikaraujobrendt
andauthored
fix(view): don't throw when using a <table> element (#1133)
Co-authored-by: brendt <[email protected]>
1 parent 30f012d commit 5a05f6d

File tree

6 files changed

+90
-12
lines changed

6 files changed

+90
-12
lines changed

phpstan.neon.dist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ parameters:
2929
message: '#.*uninitialized readonly property \$composer*#'
3030
-
3131
message: '#.*uninitialized readonly property \$stubFileGenerator*#'
32-
-
33-
message: '#.*undefined method Dom*#'
3432

3533
disallowedFunctionCalls:
3634
-

src/Tempest/View/src/Elements/ElementFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ private function makeElement(Token $token, ?Element $parent): ?Element
8888
$element = new ViewComponentElement(
8989
environment: $this->appConfig->environment,
9090
compiler: $this->compiler,
91+
elementFactory: $this,
9192
viewComponent: $viewComponentClass,
9293
attributes: $attributes,
9394
);

src/Tempest/View/src/Elements/ViewComponentElement.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Tempest\Core\Environment;
99
use Tempest\View\Element;
1010
use Tempest\View\Parser\TempestViewCompiler;
11+
use Tempest\View\Parser\TempestViewParser;
1112
use Tempest\View\Slot;
1213
use Tempest\View\ViewComponent;
1314

@@ -25,6 +26,7 @@ final class ViewComponentElement implements Element
2526
public function __construct(
2627
private readonly Environment $environment,
2728
private readonly TempestViewCompiler $compiler,
29+
private readonly ElementFactory $elementFactory,
2830
private readonly ViewComponent $viewComponent,
2931
array $attributes,
3032
) {
@@ -100,12 +102,9 @@ public function compile(): string
100102
replace: function ($matches) {
101103
$closingTag = '</' . $matches['tag'] . '>';
102104

103-
$html = $matches[0] . $closingTag;
105+
$ast = TempestViewParser::ast($matches[0] . $closingTag);
104106

105-
$dom = HTMLDocument::createFromString($html, LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR | HTML_NO_DEFAULT_NS);
106-
107-
/** @var \Dom\HTMLElement $element */
108-
$element = $dom->childNodes[0];
107+
$element = $this->elementFactory->make($ast[0]);
109108

110109
foreach (['class', 'style', 'id'] as $attributeName) {
111110
if (! isset($this->dataAttributes[$attributeName])) {
@@ -125,12 +124,12 @@ public function compile(): string
125124
}
126125

127126
$element->setAttribute(
128-
qualifiedName: $attributeName,
127+
name: $attributeName,
129128
value: $value,
130129
);
131130
}
132131

133-
return str($element->ownerDocument->saveHTML($element))->replaceLast($closingTag, '');
132+
return str($element->compile())->replaceLast($closingTag, '');
134133
},
135134
)
136135
->prepend(

src/Tempest/View/src/Parser/TempestViewAst.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
namespace Tempest\View\Parser;
44

5-
use ArrayIterator;
5+
use ArrayAccess;
66
use IteratorAggregate;
77
use Traversable;
88

9-
final class TempestViewAst implements IteratorAggregate
9+
final class TempestViewAst implements IteratorAggregate, ArrayAccess
1010
{
1111
public function __construct(
1212
private(set) TokenCollection $tokens = new TokenCollection(),
@@ -31,4 +31,24 @@ public function getIterator(): Traversable
3131
{
3232
return $this->tokens->getIterator();
3333
}
34+
35+
public function offsetExists(mixed $offset): bool
36+
{
37+
return $this->tokens->offsetExists($offset);
38+
}
39+
40+
public function offsetGet(mixed $offset): mixed
41+
{
42+
return $this->tokens->offsetGet($offset);
43+
}
44+
45+
public function offsetSet(mixed $offset, mixed $value): void
46+
{
47+
$this->tokens->offsetSet($offset, $value);
48+
}
49+
50+
public function offsetUnset(mixed $offset): void
51+
{
52+
$this->tokens->offsetUnset($offset);
53+
}
3454
}

src/Tempest/View/src/Parser/TokenCollection.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
namespace Tempest\View\Parser;
44

5+
use ArrayAccess;
56
use ArrayIterator;
67
use IteratorAggregate;
78
use Traversable;
89

910
/**
1011
* @implements IteratorAggregate<\Tempest\View\Parser\Token>
1112
*/
12-
final class TokenCollection implements IteratorAggregate
13+
final class TokenCollection implements IteratorAggregate, ArrayAccess
1314
{
1415
public function __construct(
1516
private array $tokens = [],
@@ -36,4 +37,24 @@ public function __debugInfo(): array
3637
),
3738
];
3839
}
40+
41+
public function offsetExists(mixed $offset): bool
42+
{
43+
return $this->tokens[$offset] ?? false;
44+
}
45+
46+
public function offsetGet(mixed $offset): mixed
47+
{
48+
return $this->tokens[$offset] ?? null;
49+
}
50+
51+
public function offsetSet(mixed $offset, mixed $value): void
52+
{
53+
$this->tokens[$offset] = $value;
54+
}
55+
56+
public function offsetUnset(mixed $offset): void
57+
{
58+
unset($this->tokens[$offset]);
59+
}
3960
}

tests/Integration/View/ViewComponentTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,45 @@ public function test_slots_with_hyphens(): void
752752
HTML, $html);
753753
}
754754

755+
public function test_nested_table_components(): void
756+
{
757+
$this->registerViewComponent('x-my-table-thead', '<thead><x-slot/></thead>');
758+
$this->registerViewComponent('x-my-table-tbody', '<tbody><x-slot/></tbody>');
759+
$this->registerViewComponent('x-my-table-tr', '<tr><x-slot/></tr>');
760+
$this->registerViewComponent('x-my-table-td', '<td><x-slot/></td>');
761+
$this->registerViewComponent('x-my-table-th', '<th><x-slot/></th>');
762+
763+
$html = $this->render(<<<'HTML'
764+
<table>
765+
<x-my-table-thead>
766+
<x-my-table-tr>
767+
<x-my-table-th>Header 1</x-my-table-th>
768+
</x-my-table-tr>
769+
</x-my-table-thead>
770+
<x-my-table-tbody>
771+
<x-my-table-tr>
772+
<x-my-table-td>Row 1, Cell 1</x-my-table-td>
773+
</x-my-table-tr>
774+
</x-my-table-tbody>
775+
</table>
776+
HTML);
777+
778+
$this->assertSnippetsMatch(<<<'HTML'
779+
<table>
780+
<thead>
781+
<tr>
782+
<th>Header1</th>
783+
</tr>
784+
</thead>
785+
<tbody>
786+
<tr>
787+
<td>Row 1, Cell 1</td>
788+
</tr>
789+
</tbody>
790+
</table>
791+
HTML, $html);
792+
}
793+
755794
private function assertSnippetsMatch(string $expected, string $actual): void
756795
{
757796
$expected = str_replace([PHP_EOL, ' '], '', $expected);

0 commit comments

Comments
 (0)