Skip to content

Commit 06227e7

Browse files
committed
Allow to disable nested attributes with !
1 parent 4d1dbf9 commit 06227e7

File tree

5 files changed

+66
-2
lines changed

5 files changed

+66
-2
lines changed

src/TwigComponent/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## 2.21.0
4+
5+
- Allow to disable nested attributes with !
6+
37
## 2.20.0
48

59
- Add Anonymous Component support for 3rd-party bundles #2019

src/TwigComponent/src/ComponentAttributes.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
*/
2222
final class ComponentAttributes implements \Stringable, \IteratorAggregate, \Countable
2323
{
24-
private const NESTED_REGEX = '#^([\w-]+):(.+)$#';
24+
private const NESTED_REGEX = '#^([\w-]+):(.+)(?<!!)$#';
2525

2626
/** @var array<string,true> */
2727
private array $rendered = [];
@@ -64,6 +64,10 @@ function (string $carry, string $key) {
6464
$value = 'true';
6565
}
6666

67+
if (str_ends_with($key, '!')) {
68+
$key = substr($key, 0, -1);
69+
}
70+
6771
return match ($value) {
6872
true => "{$carry} {$key}",
6973
false => $carry,

src/TwigComponent/src/Twig/TwigPreLexer.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public function preLexComponents(string $input): string
200200
private function consumeComponentName(?string $customExceptionMessage = null): string
201201
{
202202
$start = $this->position;
203-
while ($this->position < $this->length && preg_match('/[A-Za-z0-9_:@\-.]/', $this->input[$this->position])) {
203+
while ($this->position < $this->length && preg_match('/[A-Za-z0-9_:@!\-.]/', $this->input[$this->position])) {
204204
++$this->position;
205205
}
206206

@@ -253,6 +253,10 @@ private function consumeAttributes(string $componentName): string
253253

254254
$key = $this->consumeAttributeName($componentName);
255255

256+
if (str_ends_with($key, '!')) {
257+
$isAttributeDynamic = false;
258+
}
259+
256260
// <twig:component someProp> -> someProp: true
257261
if (!$this->check('=')) {
258262
// don't allow "<twig:component :someProp>"

src/TwigComponent/tests/Integration/ComponentExtensionTest.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,44 @@ public function testRenderingComponentWithNestedAttributes(): void
316316
);
317317
}
318318

319+
public function testRenderingComponentWithNestedAndNotNestedAttributes(): void
320+
{
321+
$output = $this->renderComponent('NestedAttributes');
322+
323+
$this->assertSame(<<<HTML
324+
<main>
325+
<div>
326+
<span>
327+
<div/>
328+
329+
</span>
330+
</div>
331+
</main>
332+
HTML,
333+
trim($output)
334+
);
335+
336+
$output = $this->renderComponent('NestedAttributes', [
337+
'class' => 'foo',
338+
'x-bind:class!' => 'alpine',
339+
':class!' => 'alpine',
340+
'title:span:class' => 'baz',
341+
]);
342+
343+
$this->assertSame(<<<HTML
344+
<main class="foo" x-bind:class="alpine" :class="alpine">
345+
<div>
346+
<span class="baz">
347+
<div/>
348+
349+
</span>
350+
</div>
351+
</main>
352+
HTML,
353+
trim($output)
354+
);
355+
}
356+
319357
public function testRenderingHtmlSyntaxComponentWithNestedAttributes(): void
320358
{
321359
$output = self::getContainer()

src/TwigComponent/tests/Unit/ComponentAttributesTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,20 @@ public function testNestedAttributes(): void
259259
$this->assertSame('', (string) $attributes->nested('invalid'));
260260
}
261261

262+
public function testNotNestedAttributes(): void
263+
{
264+
$attributes = new ComponentAttributes([
265+
'class' => 'foo',
266+
'x-bind:class!' => 'alpine',
267+
':class!' => 'alpine',
268+
'title:span:class' => 'baz',
269+
]);
270+
271+
$this->assertSame(' class="foo" x-bind:class="alpine" :class="alpine"', (string) $attributes);
272+
$this->assertSame(' class="baz"', (string) $attributes->nested('title')->nested('span'));
273+
$this->assertSame('', (string) $attributes->nested('invalid'));
274+
}
275+
262276
public function testConvertTrueAriaAttributeValue(): void
263277
{
264278
$attributes = new ComponentAttributes([

0 commit comments

Comments
 (0)