Skip to content

Commit f60079d

Browse files
committed
bug #1298 [TwigComponent] Allow trailing coma in "props" tags (smnandre)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [TwigComponent] Allow trailing coma in "props" tags | Q | A | ------------- | --- | Bug fix? | no | New feature? | not really | Issues | Fix #1286 | License | MIT Allow trailing comas in props tag ```twig {% props foo = 'foo', bar = 'bar', %} ``` Commits ------- 799df54 [TwigComponent] Allow trailing coma in "props" tags
2 parents ff864e6 + 799df54 commit f60079d

File tree

6 files changed

+167
-3
lines changed

6 files changed

+167
-3
lines changed

src/TwigComponent/src/Twig/PropsTokenParser.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,11 @@ public function parse(Token $token): Node
3939
$names[] = $name;
4040

4141
if (!$stream->nextIf(Token::PUNCTUATION_TYPE)) {
42+
$stream->expect(\Twig\Token::BLOCK_END_TYPE);
4243
break;
4344
}
4445
}
4546

46-
$stream->expect(\Twig\Token::BLOCK_END_TYPE);
47-
4847
return new PropsNode($names, $values, $token->getLine(), $token->getValue());
4948
}
5049

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<twig:CommaProps bar="bar" />
2+
<twig:CommaProps foo="FOO" bar="123" foobar="456" />

src/TwigComponent/tests/Fixtures/templates/components/Button.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
<button {{ attributes.defaults({class: primary ? 'primary' : 'secondary'}) }}>
44
{{ label }}
5-
</button>
5+
</button>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{% props
2+
foo = 'foo',
3+
bar,
4+
foobar = 'foobar',
5+
%}
6+
7+
<p>Hello {{ foo }}, {{ bar }}, and {{ foobar }}</p>

src/TwigComponent/tests/Integration/ComponentExtensionTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ public function testComponentPropsOverwriteContextValue(): void
208208
$this->assertStringContainsString('<p>foo</p>', $output);
209209
}
210210

211+
public function testComponentPropsWithTrailingComma(): void
212+
{
213+
$output = self::getContainer()->get(Environment::class)->render('anonymous_component_props_trailing_comma.html.twig');
214+
215+
$this->assertStringContainsString('Hello foo, bar, and foobar', $output);
216+
$this->assertStringContainsString('Hello FOO, 123, and 456', $output);
217+
}
218+
211219
private function renderComponent(string $name, array $data = []): string
212220
{
213221
return self::getContainer()->get(Environment::class)->render('render_component.html.twig', [
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\TwigComponent\Tests\Integration\Twig;
13+
14+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
15+
use Symfony\UX\TwigComponent\Twig\PropsNode;
16+
use Twig\Environment;
17+
use Twig\Loader\ArrayLoader;
18+
use Twig\Node\TextNode;
19+
use Twig\Source;
20+
21+
/**
22+
* @author Simon André <[email protected]>
23+
*
24+
* @internal
25+
*/
26+
class ComponentPropsParserTest extends KernelTestCase
27+
{
28+
/**
29+
* @dataProvider providePropsData
30+
*/
31+
public function testPropsData(string $template, array $props, string $text): void
32+
{
33+
$loader = new ArrayLoader(['template' => $template]);
34+
35+
/** @var Environment $twig */
36+
$twig = self::getContainer()->get(Environment::class);
37+
$twig->setLoader($loader);
38+
39+
$tokenStream = $twig->tokenize(new Source($template, 'template'));
40+
$foo = $twig->parse($tokenStream);
41+
42+
$body = $foo->getNode('body')->getNode('0');
43+
$this->assertTrue($body->hasNode(0));
44+
45+
$propsNode = $body->getNode(0);
46+
$this->assertInstanceOf(PropsNode::class, $propsNode);
47+
$this->assertTrue($propsNode->hasAttribute('names'));
48+
49+
foreach ($props as $name => $value) {
50+
$this->assertContains($name, $propsNode->getAttribute('names'));
51+
if (null === $value) {
52+
$this->assertFalse($propsNode->hasNode($name));
53+
continue;
54+
}
55+
$this->assertTrue($propsNode->hasNode($name));
56+
$this->assertTrue($propsNode->getNode($name)->hasAttribute('value'));
57+
$this->assertSame($value, $propsNode->getNode($name)->getAttribute('value'));
58+
}
59+
60+
$this->assertTrue($body->hasNode(1));
61+
$this->assertInstanceOf(TextNode::class, $body->getNode(1));
62+
$this->assertTrue($body->getNode(1)->hasAttribute('data'));
63+
$this->assertSame($text, $body->getNode(1)->getAttribute('data'));
64+
}
65+
66+
/**
67+
* @return iterable<string, array{0: string, 1: array<string, string|int|null>, 2: string}>
68+
*/
69+
public static function providePropsData(): iterable
70+
{
71+
yield 'One Prop with value' => [
72+
'{% props propA=123 %} foo ',
73+
[
74+
'propA' => 123,
75+
],
76+
' foo ',
77+
];
78+
yield 'One Prop without value' => [
79+
'{% props propA %} foo ',
80+
[
81+
'propA' => null,
82+
],
83+
' foo ',
84+
];
85+
yield 'No Props with values' => [
86+
'{% props propA, propB %} foo ',
87+
[
88+
'propA' => null,
89+
'propB' => null,
90+
],
91+
' foo ',
92+
];
93+
yield 'All Props with values' => [
94+
'{% props propA=1, propB=2 %} foo ',
95+
[
96+
'propA' => 1,
97+
'propB' => 2,
98+
],
99+
' foo ',
100+
];
101+
yield 'Some Props with values' => [
102+
'{% props propA, propB=2 %} foo ',
103+
[
104+
'propA' => null,
105+
'propB' => 2,
106+
],
107+
' foo ',
108+
];
109+
yield 'One Prop with value and trailing comma' => [
110+
'{% props propA=123, %} foo ',
111+
[
112+
'propA' => 123,
113+
],
114+
' foo ',
115+
];
116+
yield 'One Prop without value and trailing comma' => [
117+
'{% props propA, %} foo ',
118+
[
119+
'propA' => null,
120+
],
121+
' foo ',
122+
];
123+
yield 'No Props with values and trailing comma' => [
124+
'{% props propA, propB, %} foo ',
125+
[
126+
'propA' => null,
127+
'propB' => null,
128+
],
129+
' foo ',
130+
];
131+
yield 'All Props with values and trailing comma' => [
132+
'{% props propA=1, propB=2, %} foo ',
133+
[
134+
'propA' => 1,
135+
'propB' => 2,
136+
],
137+
' foo ',
138+
];
139+
yield 'Some Props with values and trailing comma' => [
140+
'{% props propA, propB=2, %} foo ',
141+
[
142+
'propA' => null,
143+
'propB' => 2,
144+
],
145+
' foo ',
146+
];
147+
}
148+
}

0 commit comments

Comments
 (0)