Skip to content

Commit 029be0c

Browse files
committed
feature #942 Stimulus controllers: allow to define outlets (jmsche)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- Stimulus controllers: allow to define outlets | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Tickets | N/A | License | MIT This PR allows to define outlets to Stimulus controllers. See https://stimulus.hotwired.dev/reference/outlets Also updated .gitignore as running PHPUnit locally generated a `.phpunit.result.cache` file. Commits ------- 21dfabd Add changelog entry 2b632f6 Stimulus outlets
2 parents 922a422 + 21dfabd commit 029be0c

File tree

6 files changed

+65
-10
lines changed

6 files changed

+65
-10
lines changed

src/StimulusBundle/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.php-cs-fixer.cache
2-
.phpunit.cache
2+
.phpunit.result.cache
33
composer.lock
44
vendor/
55
tests/fixtures/var

src/StimulusBundle/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.10.0
4+
5+
- Handle Stimulus outlets
6+
37
## 2.9.0
48

59
- Introduce the bundle

src/StimulusBundle/doc/index.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ stimulus_controller
162162

163163
This bundle ships with a special ``stimulus_controller()`` Twig function
164164
that can be used to render `Stimulus Controllers & Values`_ and `CSS Classes`_.
165+
Stimulus Controllers can also reference other controllers by using `Outlets`_.
165166

166167
For example:
167168

@@ -203,6 +204,30 @@ If you want to set CSS classes:
203204
Hello
204205
</div>
205206

207+
And with outlets:
208+
209+
.. code-block:: html+twig
210+
211+
<div {{ stimulus_controller('chart', { 'name': 'Likes', 'data': [1, 2, 3, 4] }, { 'loading': 'spinner' }, { 'other': '.target' ) }}>
212+
Hello
213+
</div>
214+
215+
<!-- would render -->
216+
<div
217+
data-controller="chart"
218+
data-chart-name-value="Likes"
219+
data-chart-data-value="&#x5B;1,2,3,4&#x5D;"
220+
data-chart-loading-class="spinner"
221+
data-chart-other-outlet=".target"
222+
>
223+
Hello
224+
</div>
225+
226+
<!-- or without values/classes -->
227+
<div {{ stimulus_controller('chart', controllerOutlets = { 'other': '.target' }) }}>
228+
Hello
229+
</div>
230+
206231
Any non-scalar values (like ``data: [1, 2, 3, 4]``) are JSON-encoded. And all
207232
values are properly escaped (the string ``&#x5B;`` is an escaped
208233
``[`` character, so the attribute is really ``[1,2,3,4]``).
@@ -478,6 +503,7 @@ it will normalize it:
478503
.. _`AssetMapper`: https://symfony.com/doc/current/frontend/asset-mapper.html
479504
.. _`Stimulus Controllers & Values`: https://stimulus.hotwired.dev/reference/values
480505
.. _`CSS Classes`: https://stimulus.hotwired.dev/reference/css-classes
506+
.. _`Outlets`: https://stimulus.hotwired.dev/reference/outlets
481507
.. _`Stimulus Actions`: https://stimulus.hotwired.dev/reference/actions
482508
.. _`parameters`: https://stimulus.hotwired.dev/reference/actions#action-parameters
483509
.. _`Stimulus Targets`: https://stimulus.hotwired.dev/reference/targets

src/StimulusBundle/src/Dto/StimulusAttributes.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function getIterator(): \Traversable
3535
return new \ArrayIterator($this->toArray());
3636
}
3737

38-
public function addController(string $controllerName, array $controllerValues = [], array $controllerClasses = []): void
38+
public function addController(string $controllerName, array $controllerValues = [], array $controllerClasses = [], array $controllerOutlets = []): void
3939
{
4040
$controllerName = $this->normalizeControllerName($controllerName);
4141
$this->controllers[] = $controllerName;
@@ -56,6 +56,12 @@ public function addController(string $controllerName, array $controllerValues =
5656

5757
$this->attributes['data-'.$controllerName.'-'.$key.'-class'] = $class;
5858
}
59+
60+
foreach ($controllerOutlets as $outlet => $selector) {
61+
$outlet = $this->normalizeKeyName($outlet);
62+
63+
$this->attributes['data-'.$controllerName.'-'.$outlet.'-outlet'] = $selector;
64+
}
5965
}
6066

6167
/**

src/StimulusBundle/src/Twig/StimulusTwigExtension.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,19 @@ public function getFilters(): array
4848
* @param string $controllerName the Stimulus controller name
4949
* @param array $controllerValues array of controller values
5050
* @param array $controllerClasses array of controller CSS classes
51+
* @param array $controllerOutlets array of controller outlets
5152
*/
52-
public function renderStimulusController(string $controllerName, array $controllerValues = [], array $controllerClasses = []): StimulusAttributes
53+
public function renderStimulusController(string $controllerName, array $controllerValues = [], array $controllerClasses = [], array $controllerOutlets = []): StimulusAttributes
5354
{
5455
$stimulusAttributes = $this->stimulusHelper->createStimulusAttributes();
55-
$stimulusAttributes->addController($controllerName, $controllerValues, $controllerClasses);
56+
$stimulusAttributes->addController($controllerName, $controllerValues, $controllerClasses, $controllerOutlets);
5657

5758
return $stimulusAttributes;
5859
}
5960

60-
public function appendStimulusController(StimulusAttributes $stimulusAttributes, string $controllerName, array $controllerValues = [], array $controllerClasses = []): StimulusAttributes
61+
public function appendStimulusController(StimulusAttributes $stimulusAttributes, string $controllerName, array $controllerValues = [], array $controllerClasses = [], array $controllerOutlets = []): StimulusAttributes
6162
{
62-
$stimulusAttributes->addController($controllerName, $controllerValues, $controllerClasses);
63+
$stimulusAttributes->addController($controllerName, $controllerValues, $controllerClasses, $controllerOutlets);
6364

6465
return $stimulusAttributes;
6566
}

src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ protected function setUp(): void
3232
/**
3333
* @dataProvider provideRenderStimulusController
3434
*/
35-
public function testRenderStimulusController(string $controllerName, array $controllerValues, array $controllerClasses, string $expectedString, array $expectedArray): void
35+
public function testRenderStimulusController(string $controllerName, array $controllerValues, array $controllerClasses, array $controllerOutlets, string $expectedString, array $expectedArray): void
3636
{
3737
$extension = new StimulusTwigExtension(new StimulusHelper($this->twig));
38-
$dto = $extension->renderStimulusController($controllerName, $controllerValues, $controllerClasses);
38+
$dto = $extension->renderStimulusController($controllerName, $controllerValues, $controllerClasses, $controllerOutlets);
3939
$this->assertSame($expectedString, (string) $dto);
4040
$this->assertSame($expectedArray, $dto->toArray());
4141
}
@@ -50,14 +50,18 @@ public static function provideRenderStimulusController(): iterable
5050
'controllerClasses' => [
5151
'second"Key"' => 'loading',
5252
],
53-
'expectedString' => 'data-controller="symfony--ux-dropzone--dropzone" data-symfony--ux-dropzone--dropzone-my-key-value="true" data-symfony--ux-dropzone--dropzone-second-key-class="loading"',
54-
'expectedArray' => ['data-controller' => 'symfony--ux-dropzone--dropzone', 'data-symfony--ux-dropzone--dropzone-my-key-value' => 'true', 'data-symfony--ux-dropzone--dropzone-second-key-class' => 'loading'],
53+
'controllerOutlets' => [
54+
'other' => '.test',
55+
],
56+
'expectedString' => 'data-controller="symfony--ux-dropzone--dropzone" data-symfony--ux-dropzone--dropzone-my-key-value="true" data-symfony--ux-dropzone--dropzone-second-key-class="loading" data-symfony--ux-dropzone--dropzone-other-outlet=".test"',
57+
'expectedArray' => ['data-controller' => 'symfony--ux-dropzone--dropzone', 'data-symfony--ux-dropzone--dropzone-my-key-value' => 'true', 'data-symfony--ux-dropzone--dropzone-second-key-class' => 'loading', 'data-symfony--ux-dropzone--dropzone-other-outlet' => '.test'],
5558
];
5659

5760
yield 'short-single-controller-no-data' => [
5861
'controllerName' => 'my-controller',
5962
'controllerValues' => [],
6063
'controllerClasses' => [],
64+
'controllerOutlets' => [],
6165
'expectedString' => 'data-controller="my-controller"',
6266
'expectedArray' => ['data-controller' => 'my-controller'],
6367
];
@@ -66,6 +70,7 @@ public static function provideRenderStimulusController(): iterable
6670
'controllerName' => 'my-controller',
6771
'controllerValues' => ['myValue' => 'scalar-value'],
6872
'controllerClasses' => [],
73+
'controllerOutlets' => [],
6974
'expectedString' => 'data-controller="my-controller" data-my-controller-my-value-value="scalar-value"',
7075
'expectedArray' => ['data-controller' => 'my-controller', 'data-my-controller-my-value-value' => 'scalar-value'],
7176
];
@@ -74,6 +79,7 @@ public static function provideRenderStimulusController(): iterable
7479
'controllerName' => 'false-controller',
7580
'controllerValues' => ['isEnabled' => false],
7681
'controllerClasses' => [],
82+
'controllerOutlets' => [],
7783
'expectedString' => 'data-controller="false-controller" data-false-controller-is-enabled-value="false"',
7884
'expectedArray' => ['data-controller' => 'false-controller', 'data-false-controller-is-enabled-value' => 'false'],
7985
];
@@ -82,6 +88,7 @@ public static function provideRenderStimulusController(): iterable
8288
'controllerName' => 'true-controller',
8389
'controllerValues' => ['isEnabled' => true],
8490
'controllerClasses' => [],
91+
'controllerOutlets' => [],
8592
'expectedString' => 'data-controller="true-controller" data-true-controller-is-enabled-value="true"',
8693
'expectedArray' => ['data-controller' => 'true-controller', 'data-true-controller-is-enabled-value' => 'true'],
8794
];
@@ -90,6 +97,7 @@ public static function provideRenderStimulusController(): iterable
9097
'controllerName' => 'null-controller',
9198
'controllerValues' => ['firstName' => null],
9299
'controllerClasses' => [],
100+
'controllerOutlets' => [],
93101
'expectedString' => 'data-controller="null-controller"',
94102
'expectedArray' => ['data-controller' => 'null-controller'],
95103
];
@@ -98,9 +106,19 @@ public static function provideRenderStimulusController(): iterable
98106
'controllerName' => 'my-controller',
99107
'controllerValues' => [],
100108
'controllerClasses' => ['loading' => 'spinner'],
109+
'controllerOutlets' => [],
101110
'expectedString' => 'data-controller="my-controller" data-my-controller-loading-class="spinner"',
102111
'expectedArray' => ['data-controller' => 'my-controller', 'data-my-controller-loading-class' => 'spinner'],
103112
];
113+
114+
yield 'short-single-controller-no-data-with-outlet' => [
115+
'controllerName' => 'my-controller',
116+
'controllerValues' => [],
117+
'controllerClasses' => [],
118+
'controllerOutlets' => ['other-controller' => '.target'],
119+
'expectedString' => 'data-controller="my-controller" data-my-controller-other-controller-outlet=".target"',
120+
'expectedArray' => ['data-controller' => 'my-controller', 'data-my-controller-other-controller-outlet' => '.target'],
121+
];
104122
}
105123

106124
public function testAppendStimulusController(): void

0 commit comments

Comments
 (0)