Skip to content

Commit

Permalink
feature #110 Adding a simpler syntax for single stimulus controller e…
Browse files Browse the repository at this point in the history
…lements (weaverryan)

This PR was merged into the main branch.

Discussion
----------

Adding a simpler syntax for single stimulus controller elements

Having 1 controller per element is the main use-case. This makes that a bit nicer to do :).

```twig
<div {{ stimulus_controller('chart', { 'name': 'Likes', 'data': [1, 2, 3, 4] }) }}>
    Hello
</div>
```

Also, this makes working with controllers that don't have any values (yet) MUCH nicer:

```twig
<!-- before -->
{{ stimulus_controller({ 'chart': {} }) }}

<!-- now -->
{{ stimulus_controller('chart') }}
```

Cheers!

Commits
-------

773ead9 adding a simpler syntax for single stimulus controller elements
  • Loading branch information
weaverryan committed Feb 10, 2021
2 parents ad290c5 + 773ead9 commit e53a8f8
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 14 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ See [stimulus-bridge](https://github.com/symfony/stimulus-bridge) for more detai
For example:

```twig
<div {{ stimulus_controller({ 'chart': { 'name': 'Likes', 'data': [1, 2, 3, 4] } }) }}>
<div {{ stimulus_controller('chart', { 'name': 'Likes', 'data': [1, 2, 3, 4] }) }}>
Hello
</div>
Expand All @@ -223,6 +223,18 @@ For example:

Any non-scalar values (like `data: [1, 2, 3, 4]`) are JSON-encoded. And all
values are properly escaped (the string `&#x5B;` is an escaped
`[` character, so the attribute is really `[1,2,3,4]`).
`[` character, so the attribute is really `[1,2,3,4]`).

If you have multiple controllers on the same element, pass them all as an
associative array in the first argument:

```twig
<div {{ stimulus_controller({
'chart': { 'name': 'Likes' },
'other-controller': { },
) }}>
Hello
</div>
```

Ok, have fun!
25 changes: 22 additions & 3 deletions src/Twig/StimulusTwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,29 @@ public function getFunctions()
];
}

public function renderStimulusController(Environment $env, array $data): string
/**
* @param string|array $dataOrControllerName This can either be a map of controller names
* as keys set to their "values". Or this
* can be a string controller name and data
* is passed as the 2nd argument.
* @param array $controllerValues Array of data if a string is passed to the first argument.
* @return string
* @throws \Twig\Error\RuntimeError
*/
public function renderStimulusController(Environment $env, $dataOrControllerName, array $controllerValues = []): string
{
if (!$data) {
return '';
if (is_string($dataOrControllerName)) {
$data = [$dataOrControllerName => $controllerValues];
} else {
if ($controllerValues) {
throw new \InvalidArgumentException('You cannot pass an array to the first and second argument of stimulus_controller(): check the documentation.');
}

$data = $dataOrControllerName;

if (!$data) {
return '';
}
}

$controllers = [];
Expand Down
37 changes: 28 additions & 9 deletions tests/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,79 +182,98 @@ public function testAutowireDefaultBuildArgument()
public function provideRenderStimulusController()
{
yield 'empty' => [
'data' => [],
'dataOrControllerName' => [],
'controllerValues' => [],
'expected' => '',
];

yield 'single-controller-no-data' => [
'data' => [
'dataOrControllerName' => [
'my-controller' => [],
],
'controllerValues' => [],
'expected' => 'data-controller="my-controller"',
];

yield 'single-controller-scalar-data' => [
'data' => [
'dataOrControllerName' => [
'my-controller' => [
'myValue' => 'scalar-value',
],
],
'controllerValues' => [],
'expected' => 'data-controller="my-controller" data-my-controller-my-value-value="scalar-value"',
];

yield 'single-controller-typed-data' => [
'data' => [
'dataOrControllerName' => [
'my-controller' => [
'boolean' => true,
'number' => 4,
'string' => 'str',
],
],
'controllerValues' => [],
'expected' => 'data-controller="my-controller" data-my-controller-boolean-value="1" data-my-controller-number-value="4" data-my-controller-string-value="str"',
];

yield 'single-controller-nested-data' => [
'data' => [
'dataOrControllerName' => [
'my-controller' => [
'myValue' => ['nested' => 'array'],
],
],
'controllerValues' => [],
'expected' => 'data-controller="my-controller" data-my-controller-my-value-value="&#x7B;&quot;nested&quot;&#x3A;&quot;array&quot;&#x7D;"',
];

yield 'multiple-controllers-scalar-data' => [
'data' => [
'dataOrControllerName' => [
'my-controller' => [
'myValue' => 'scalar-value',
],
'another-controller' => [
'anotherValue' => 'scalar-value 2',
],
],
'controllerValues' => [],
'expected' => 'data-controller="my-controller another-controller" data-my-controller-my-value-value="scalar-value" data-another-controller-another-value-value="scalar-value&#x20;2"',
];

yield 'normalize-names' => [
'data' => [
'dataOrControllerName' => [
'@symfony/ux-dropzone/dropzone' => [
'my"Key"' => true,
],
],
'controllerValues' => [],
'expected' => 'data-controller="symfony--ux-dropzone--dropzone" data-symfony--ux-dropzone--dropzone-my-key-value="1"',
];

yield 'short-single-controller-no-data' => [
'dataOrControllerName' => 'my-controller',
'controllerValues' => [],
'expected' => 'data-controller="my-controller"',
];

yield 'short-single-controller-with-data' => [
'dataOrControllerName' => 'my-controller',
'controllerValues' => ['myValue' => 'scalar-value'],
'expected' => 'data-controller="my-controller" data-my-controller-my-value-value="scalar-value"',
];
}

/**
* @dataProvider provideRenderStimulusController
*/
public function testRenderStimulusController(array $data, string $expected)
public function testRenderStimulusController($dataOrControllerName, array $controllerValues, string $expected)
{
$kernel = new WebpackEncoreIntegrationTestKernel(true);
$kernel->boot();
$twig = $this->getTwigEnvironmentFromBootedKernel($kernel);

$extension = new StimulusTwigExtension();
$this->assertSame($expected, $extension->renderStimulusController($twig, $data));
$this->assertSame($expected, $extension->renderStimulusController($twig, $dataOrControllerName, $controllerValues));
}

private function getContainerFromBootedKernel(WebpackEncoreIntegrationTestKernel $kernel)
Expand Down

0 comments on commit e53a8f8

Please sign in to comment.