From 2b632f6497ad275b8cdc3cc80473575cf344ecd4 Mon Sep 17 00:00:00 2001 From: jmsche Date: Wed, 14 Jun 2023 14:02:05 +0200 Subject: [PATCH 1/2] Stimulus outlets --- src/StimulusBundle/.gitignore | 2 +- src/StimulusBundle/doc/index.rst | 26 +++++++++++++++++++ .../src/Dto/StimulusAttributes.php | 8 +++++- .../src/Twig/StimulusTwigExtension.php | 9 ++++--- .../tests/Twig/StimulusTwigExtensionTest.php | 26 ++++++++++++++++--- 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/src/StimulusBundle/.gitignore b/src/StimulusBundle/.gitignore index f3ee547e3df..d776edf227a 100644 --- a/src/StimulusBundle/.gitignore +++ b/src/StimulusBundle/.gitignore @@ -1,5 +1,5 @@ .php-cs-fixer.cache -.phpunit.cache +.phpunit.result.cache composer.lock vendor/ tests/fixtures/var diff --git a/src/StimulusBundle/doc/index.rst b/src/StimulusBundle/doc/index.rst index 3f9aee5737f..5df26026d8b 100644 --- a/src/StimulusBundle/doc/index.rst +++ b/src/StimulusBundle/doc/index.rst @@ -162,6 +162,7 @@ stimulus_controller This bundle ships with a special ``stimulus_controller()`` Twig function that can be used to render `Stimulus Controllers & Values`_ and `CSS Classes`_. +Stimulus Controllers can also reference other controllers by using `Outlets`_. For example: @@ -203,6 +204,30 @@ If you want to set CSS classes: Hello +And with outlets: + +.. code-block:: html+twig + +
+ Hello +
+ + +
+ Hello +
+ + +
+ Hello +
+ Any non-scalar values (like ``data: [1, 2, 3, 4]``) are JSON-encoded. And all values are properly escaped (the string ``[`` is an escaped ``[`` character, so the attribute is really ``[1,2,3,4]``). @@ -478,6 +503,7 @@ it will normalize it: .. _`AssetMapper`: https://symfony.com/doc/current/frontend/asset-mapper.html .. _`Stimulus Controllers & Values`: https://stimulus.hotwired.dev/reference/values .. _`CSS Classes`: https://stimulus.hotwired.dev/reference/css-classes +.. _`Outlets`: https://stimulus.hotwired.dev/reference/outlets .. _`Stimulus Actions`: https://stimulus.hotwired.dev/reference/actions .. _`parameters`: https://stimulus.hotwired.dev/reference/actions#action-parameters .. _`Stimulus Targets`: https://stimulus.hotwired.dev/reference/targets diff --git a/src/StimulusBundle/src/Dto/StimulusAttributes.php b/src/StimulusBundle/src/Dto/StimulusAttributes.php index 5b2abd01d80..04a86b63f36 100644 --- a/src/StimulusBundle/src/Dto/StimulusAttributes.php +++ b/src/StimulusBundle/src/Dto/StimulusAttributes.php @@ -35,7 +35,7 @@ public function getIterator(): \Traversable return new \ArrayIterator($this->toArray()); } - public function addController(string $controllerName, array $controllerValues = [], array $controllerClasses = []): void + public function addController(string $controllerName, array $controllerValues = [], array $controllerClasses = [], array $controllerOutlets = []): void { $controllerName = $this->normalizeControllerName($controllerName); $this->controllers[] = $controllerName; @@ -56,6 +56,12 @@ public function addController(string $controllerName, array $controllerValues = $this->attributes['data-'.$controllerName.'-'.$key.'-class'] = $class; } + + foreach ($controllerOutlets as $outlet => $selector) { + $outlet = $this->normalizeKeyName($outlet); + + $this->attributes['data-'.$controllerName.'-'.$outlet.'-outlet'] = $selector; + } } /** diff --git a/src/StimulusBundle/src/Twig/StimulusTwigExtension.php b/src/StimulusBundle/src/Twig/StimulusTwigExtension.php index f5bf828ce65..b2f9290c476 100644 --- a/src/StimulusBundle/src/Twig/StimulusTwigExtension.php +++ b/src/StimulusBundle/src/Twig/StimulusTwigExtension.php @@ -48,18 +48,19 @@ public function getFilters(): array * @param string $controllerName the Stimulus controller name * @param array $controllerValues array of controller values * @param array $controllerClasses array of controller CSS classes + * @param array $controllerOutlets array of controller outlets */ - public function renderStimulusController(string $controllerName, array $controllerValues = [], array $controllerClasses = []): StimulusAttributes + public function renderStimulusController(string $controllerName, array $controllerValues = [], array $controllerClasses = [], array $controllerOutlets = []): StimulusAttributes { $stimulusAttributes = $this->stimulusHelper->createStimulusAttributes(); - $stimulusAttributes->addController($controllerName, $controllerValues, $controllerClasses); + $stimulusAttributes->addController($controllerName, $controllerValues, $controllerClasses, $controllerOutlets); return $stimulusAttributes; } - public function appendStimulusController(StimulusAttributes $stimulusAttributes, string $controllerName, array $controllerValues = [], array $controllerClasses = []): StimulusAttributes + public function appendStimulusController(StimulusAttributes $stimulusAttributes, string $controllerName, array $controllerValues = [], array $controllerClasses = [], array $controllerOutlets = []): StimulusAttributes { - $stimulusAttributes->addController($controllerName, $controllerValues, $controllerClasses); + $stimulusAttributes->addController($controllerName, $controllerValues, $controllerClasses, $controllerOutlets); return $stimulusAttributes; } diff --git a/src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php b/src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php index fc92807d5ce..92b644675c6 100644 --- a/src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php +++ b/src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php @@ -32,10 +32,10 @@ protected function setUp(): void /** * @dataProvider provideRenderStimulusController */ - public function testRenderStimulusController(string $controllerName, array $controllerValues, array $controllerClasses, string $expectedString, array $expectedArray): void + public function testRenderStimulusController(string $controllerName, array $controllerValues, array $controllerClasses, array $controllerOutlets, string $expectedString, array $expectedArray): void { $extension = new StimulusTwigExtension(new StimulusHelper($this->twig)); - $dto = $extension->renderStimulusController($controllerName, $controllerValues, $controllerClasses); + $dto = $extension->renderStimulusController($controllerName, $controllerValues, $controllerClasses, $controllerOutlets); $this->assertSame($expectedString, (string) $dto); $this->assertSame($expectedArray, $dto->toArray()); } @@ -50,14 +50,18 @@ public static function provideRenderStimulusController(): iterable 'controllerClasses' => [ 'second"Key"' => 'loading', ], - '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"', - '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'], + 'controllerOutlets' => [ + 'other' => '.test', + ], + '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"', + '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'], ]; yield 'short-single-controller-no-data' => [ 'controllerName' => 'my-controller', 'controllerValues' => [], 'controllerClasses' => [], + 'controllerOutlets' => [], 'expectedString' => 'data-controller="my-controller"', 'expectedArray' => ['data-controller' => 'my-controller'], ]; @@ -66,6 +70,7 @@ public static function provideRenderStimulusController(): iterable 'controllerName' => 'my-controller', 'controllerValues' => ['myValue' => 'scalar-value'], 'controllerClasses' => [], + 'controllerOutlets' => [], 'expectedString' => 'data-controller="my-controller" data-my-controller-my-value-value="scalar-value"', 'expectedArray' => ['data-controller' => 'my-controller', 'data-my-controller-my-value-value' => 'scalar-value'], ]; @@ -74,6 +79,7 @@ public static function provideRenderStimulusController(): iterable 'controllerName' => 'false-controller', 'controllerValues' => ['isEnabled' => false], 'controllerClasses' => [], + 'controllerOutlets' => [], 'expectedString' => 'data-controller="false-controller" data-false-controller-is-enabled-value="false"', 'expectedArray' => ['data-controller' => 'false-controller', 'data-false-controller-is-enabled-value' => 'false'], ]; @@ -82,6 +88,7 @@ public static function provideRenderStimulusController(): iterable 'controllerName' => 'true-controller', 'controllerValues' => ['isEnabled' => true], 'controllerClasses' => [], + 'controllerOutlets' => [], 'expectedString' => 'data-controller="true-controller" data-true-controller-is-enabled-value="true"', 'expectedArray' => ['data-controller' => 'true-controller', 'data-true-controller-is-enabled-value' => 'true'], ]; @@ -90,6 +97,7 @@ public static function provideRenderStimulusController(): iterable 'controllerName' => 'null-controller', 'controllerValues' => ['firstName' => null], 'controllerClasses' => [], + 'controllerOutlets' => [], 'expectedString' => 'data-controller="null-controller"', 'expectedArray' => ['data-controller' => 'null-controller'], ]; @@ -98,9 +106,19 @@ public static function provideRenderStimulusController(): iterable 'controllerName' => 'my-controller', 'controllerValues' => [], 'controllerClasses' => ['loading' => 'spinner'], + 'controllerOutlets' => [], 'expectedString' => 'data-controller="my-controller" data-my-controller-loading-class="spinner"', 'expectedArray' => ['data-controller' => 'my-controller', 'data-my-controller-loading-class' => 'spinner'], ]; + + yield 'short-single-controller-no-data-with-outlet' => [ + 'controllerName' => 'my-controller', + 'controllerValues' => [], + 'controllerClasses' => [], + 'controllerOutlets' => ['other-controller' => '.target'], + 'expectedString' => 'data-controller="my-controller" data-my-controller-other-controller-outlet=".target"', + 'expectedArray' => ['data-controller' => 'my-controller', 'data-my-controller-other-controller-outlet' => '.target'], + ]; } public function testAppendStimulusController(): void From 21dfabd9e882a35629af70131d4e48f22d3902fa Mon Sep 17 00:00:00 2001 From: jmsche Date: Wed, 14 Jun 2023 14:03:27 +0200 Subject: [PATCH 2/2] Add changelog entry --- src/StimulusBundle/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/StimulusBundle/CHANGELOG.md b/src/StimulusBundle/CHANGELOG.md index c41847452cb..f126cafcd5d 100644 --- a/src/StimulusBundle/CHANGELOG.md +++ b/src/StimulusBundle/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 2.10.0 + +- Handle Stimulus outlets + ## 2.9.0 - Introduce the bundle