Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
8 changes: 7 additions & 1 deletion src/TwigComponent/src/ComponentAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
final class ComponentAttributes implements \Stringable, \IteratorAggregate, \Countable
{
private const NESTED_REGEX = '#^([\w-]+):(.+)$#';
private const ALPINE_REGEX = '#^x-([a-z]+):[^:]+$#';
private const VUE_REGEX = '#^v-([a-z]+):[^:]+$#';

/** @var array<string,true> */
private array $rendered = [];
Expand All @@ -41,7 +43,11 @@ public function __toString(): string
fn (string $key) => !isset($this->rendered[$key])
),
function (string $carry, string $key) {
if (preg_match(self::NESTED_REGEX, $key)) {
if (
preg_match(self::NESTED_REGEX, $key)
&& !preg_match(self::ALPINE_REGEX, $key)
&& !preg_match(self::VUE_REGEX, $key)
) {
return $carry;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div{{ attributes }}></div>
52 changes: 52 additions & 0 deletions src/TwigComponent/tests/Integration/ComponentExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,58 @@ public function testRenderingComponentWithNestedAttributes(): void
);
}

/**
* @dataProvider providePrefixedAttributesCases
*/
public function testRenderPrefixedAttributes(string $attributes, bool $expectContains): void
{
/** @var Environment $twig */
$twig = self::getContainer()->get(Environment::class);
$template = $twig->createTemplate(\sprintf('<twig:PrefixedAttributes %s/>', $attributes));

if ($expectContains) {
self::assertStringContainsString($attributes, trim($template->render()));

return;
}

self::assertStringNotContainsString($attributes, trim($template->render()));
}

/**
* @return iterable<array{0: string, 1: bool}>
*/
public static function providePrefixedAttributesCases(): iterable
{
// General
yield ['x:men', false]; // Nested
yield ['x:men="u"', false]; // Nested
yield ['x-men', true];
yield ['x-men="u"', true];

// AlpineJS
yield ['x-click="count++"', true];
yield ['x-on:click="count++"', true];
yield ['@click="open"', true];
// Not AlpineJS
yield ['z-click="count++"', true];
yield ['z-on:click="count++"', false]; // Nested

// Stencil
yield ['onClick="count++"', true];
yield ['@onClick="count++"', true];

// VueJs
yield ['v-model="message"', true];
yield ['v-bind:id="dynamicId"', true];
yield ['v-bind:id', true];
yield ['@submit.prevent="onSubmit"', true];
// Not VueJs
yield ['z-model="message"', true];
yield ['z-bind:id="dynamicId"', false]; // Nested
yield ['z-bind:id', false]; // Nested
}

public function testRenderingHtmlSyntaxComponentWithNestedAttributes(): void
{
$output = self::getContainer()
Expand Down
13 changes: 13 additions & 0 deletions src/TwigComponent/tests/Unit/ComponentAttributesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,19 @@ public function testNestedAttributes(): void
$this->assertSame('', (string) $attributes->nested('invalid'));
}

public function testPrefixedAttributes(): void
{
$attributes = new ComponentAttributes([
'x-click' => 'x+',
'title:x-click' => 'title:x+',
]);

$this->assertSame(' x-click="x+"', (string) $attributes);
$this->assertSame(' x-click="title:x+"', (string) $attributes->nested('title'));
$this->assertSame('', (string) $attributes->nested('title')->nested('span'));
$this->assertSame('', (string) $attributes->nested('invalid'));
}

public function testConvertTrueAriaAttributeValue(): void
{
$attributes = new ComponentAttributes([
Expand Down