Skip to content

Commit 35e65c7

Browse files
committed
feat(twig): Allow nested attributes
1 parent a2418ef commit 35e65c7

File tree

6 files changed

+113
-0
lines changed

6 files changed

+113
-0
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+
## Unreleased
4+
5+
- Added nested attribute support.
6+
37
## 2.13.0
48

59
- [BC BREAK] Add component metadata to `PreMountEvent` and `PostMountEvent`

src/TwigComponent/src/ComponentAttributes.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
*/
2222
final class ComponentAttributes
2323
{
24+
private const NESTED_REGEX = '#^([\w-]+):(.+)$#';
25+
2426
/**
2527
* @param array<string, string|bool> $attributes
2628
*/
@@ -33,6 +35,10 @@ public function __toString(): string
3335
return array_reduce(
3436
array_keys($this->attributes),
3537
function (string $carry, string $key) {
38+
if (preg_match(self::NESTED_REGEX, $key)) {
39+
return $carry;
40+
}
41+
3642
$value = $this->attributes[$key];
3743

3844
if (!\is_scalar($value) && null !== $value) {
@@ -157,4 +163,17 @@ public function remove($key): self
157163

158164
return new self($attributes);
159165
}
166+
167+
public function nested(string $namespace): self
168+
{
169+
$attributes = [];
170+
171+
foreach ($this->attributes as $key => $value) {
172+
if (preg_match(self::NESTED_REGEX, $key, $matches) && $namespace === $matches[1]) {
173+
$attributes[$matches[2]] = $value;
174+
}
175+
}
176+
177+
return new self($attributes);
178+
}
160179
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<main{{ attributes }}>
2+
<div{{ attributes.nested('title') }}>
3+
<span{{ attributes.nested('title').nested('span') }}>title</span>
4+
</div>
5+
</main>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<twig:NestedAttributes />

src/TwigComponent/tests/Integration/ComponentExtensionTest.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,76 @@ public function testComponentPropsWithTrailingComma(): void
216216
$this->assertStringContainsString('Hello FOO, 123, and 456', $output);
217217
}
218218

219+
public function testRenderingComponentWithNestedAttributes(): void
220+
{
221+
$output = $this->renderComponent('NestedAttributes');
222+
223+
$this->assertSame(
224+
<<<HTML
225+
<main>
226+
<div>
227+
<span>title</span>
228+
</div>
229+
</main>
230+
HTML,
231+
trim($output)
232+
);
233+
234+
$output = $this->renderComponent('NestedAttributes', [
235+
'class' => 'foo',
236+
'title:class' => 'bar',
237+
'title:span:class' => 'baz',
238+
]);
239+
240+
$this->assertSame(
241+
<<<HTML
242+
<main class="foo">
243+
<div class="bar">
244+
<span class="baz">title</span>
245+
</div>
246+
</main>
247+
HTML,
248+
trim($output)
249+
);
250+
}
251+
252+
public function testRenderingHtmlSyntaxComponentWithNestedAttributes(): void
253+
{
254+
$output = self::getContainer()
255+
->get(Environment::class)
256+
->createTemplate('<twig:NestedAttributes />')
257+
->render()
258+
;
259+
260+
$this->assertSame(
261+
<<<HTML
262+
<main>
263+
<div>
264+
<span>title</span>
265+
</div>
266+
</main>
267+
HTML,
268+
trim($output)
269+
);
270+
271+
$output = self::getContainer()
272+
->get(Environment::class)
273+
->createTemplate('<twig:NestedAttributes class="foo" title:class="bar" title:span:class="baz" />')
274+
->render()
275+
;
276+
277+
$this->assertSame(
278+
<<<HTML
279+
<main class="foo">
280+
<div class="bar">
281+
<span class="baz">title</span>
282+
</div>
283+
</main>
284+
HTML,
285+
trim($output)
286+
);
287+
}
288+
219289
private function renderComponent(string $name, array $data = []): string
220290
{
221291
return self::getContainer()->get(Environment::class)->render('render_component.html.twig', [

src/TwigComponent/tests/Unit/ComponentAttributesTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,18 @@ public function testNullBehaviour(): void
191191
$this->assertSame(['disabled' => null], $attributes->all());
192192
$this->assertSame(' disabled', (string) $attributes);
193193
}
194+
195+
public function testNestedAttributes(): void
196+
{
197+
$attributes = new ComponentAttributes([
198+
'class' => 'foo',
199+
'title:class' => 'bar',
200+
'title:span:class' => 'baz',
201+
]);
202+
203+
$this->assertSame(' class="foo"', (string) $attributes);
204+
$this->assertSame(' class="bar"', (string) $attributes->nested('title'));
205+
$this->assertSame(' class="baz"', (string) $attributes->nested('title')->nested('span'));
206+
$this->assertSame('', (string) $attributes->nested('invalid'));
207+
}
194208
}

0 commit comments

Comments
 (0)