mirror of
https://github.com/symfony/ux.git
synced 2026-03-24 00:02:21 +01:00
feature #3408 [TwigComponent] Add support for AttributeValueInterface from twig/html-extra:^3.24.0 in ComponentAttributes (Kocal)
This PR was merged into the 2.x branch.
Discussion
----------
[TwigComponent] Add support for `AttributeValueInterface` from `twig/html-extra:^3.24.0` in `ComponentAttributes`
| Q | A
| -------------- | ---
| Bug fix? | no
| New feature? | yes <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md -->
| Documentation? | no <!-- required for new features, or documentation updates -->
| Issues | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
| License | MIT
Close https://github.com/twigphp/Twig/issues/4790, replace https://github.com/twigphp/Twig/pull/4791.
This PR update `ComponentAttributes` to support `AttributeValueInterface` from Twig 3.24 with `html_attr_type` and HTML attributes merging strategy.
This helps resolve situations where merging HTML attributes needs to be more sophisticated than a simple `array_merge`.
For example in UX Toolkit, we have an issue where it's not possible to use a single `<twig:Button>` with `Dialog` and `Tooltip` triggers, both triggers define a `trigger_attrs` with some attributes that may conflict. Here a simplified version:
```twig
{%- set dialog_trigger_attrs = {
'data-action': 'click->dialog#open',
} -%}
{%- set tooltip_trigger_attrs = {
'data-action': 'mouseenter->tooltip#show mouseleave->tooltip#hide focus->tooltip#show blur->tooltip#hide',
} -%}
<twig:Button
{{ ...dialog_trigger_attrs }}
{{ ...tooltip_trigger_attrs }}
/>
```
Here, only `data-action="mouseenter->tooltip#show mouseleave->tooltip#hide focus->tooltip#show blur->tooltip#hide"` will be rendered, the value from `dialog_trigger_attrs` is purely ignored.
By supporting the HTML attributes merging strategy introduced in Twig HTML Extra 3.24, we can use the new Twig filter `html_attr_type`:
```twig
{%- set dialog_trigger_attrs = {
'data-action': 'click->dialog#open'|html_attr_type('sst'),
} -%}
{%- set tooltip_trigger_attrs = {
'data-action': 'mouseenter->tooltip#show mouseleave->tooltip#hide focus->tooltip#show blur->tooltip#hide'|html_attr_type('sst'),
} -%}
```
Combined to `html_attr_merge` filter (that return an array where some values are an instance of `Twig\Extra\Html\HtmlAttr\AttributeValueInterface`), the following example will correctly render `data-action="click->dialog#open mouseenter->tooltip#show mouseleave->tooltip#hide focus->tooltip#show blur->tooltip#hide"`:
```twig
<twig:Button
{{ ...{}|html_attr_merge(dialog_trigger_attrs, tooltip_trigger_attrs) }}
/>
```
Commits
-------
bd2410fb79 [TwigComponent] Add support for `AttributeValueInterface` from `twig/html-extra:^3.24.0` in `ComponentAttributes`
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.35
|
||||
|
||||
Add support for `AttributeValueInterface` from `twig/html-extra:^3.24.0` in `ComponentAttributes`
|
||||
|
||||
## 2.33
|
||||
|
||||
- Extended support for the `index.html.twig` template fallback when resolving namespaced anonymous components
|
||||
|
||||
@@ -41,7 +41,9 @@
|
||||
"symfony/phpunit-bridge": "^6.0|^7.0|^8.0",
|
||||
"symfony/stimulus-bundle": "^2.9.1",
|
||||
"symfony/twig-bundle": "^5.4|^6.0|^7.0|^8.0",
|
||||
"symfony/webpack-encore-bundle": "^1.15|^2.3.0"
|
||||
"symfony/webpack-encore-bundle": "^1.15|^2.3.0",
|
||||
"twig/extra-bundle": "^3.10.3",
|
||||
"twig/html-extra": "^3.10.3"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/config": "<5.4.0"
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Symfony\UX\TwigComponent;
|
||||
|
||||
use Symfony\UX\StimulusBundle\Dto\StimulusAttributes;
|
||||
use Symfony\WebpackEncoreBundle\Dto\AbstractStimulusDto;
|
||||
use Twig\Extra\Html\HtmlAttr\AttributeValueInterface;
|
||||
use Twig\Runtime\EscaperRuntime;
|
||||
|
||||
/**
|
||||
@@ -65,6 +66,10 @@ final class ComponentAttributes implements \Stringable, \IteratorAggregate, \Cou
|
||||
$value = true;
|
||||
}
|
||||
|
||||
if ($value instanceof AttributeValueInterface) {
|
||||
$value = $value->getValue();
|
||||
}
|
||||
|
||||
if (!\is_scalar($value) && !($value instanceof \Stringable)) {
|
||||
throw new \LogicException(\sprintf('A "%s" prop was passed when creating the component. No matching "%s" property or mount() argument was found, so we attempted to use this as an HTML attribute. But, the value is not a scalar (it\'s a "%s"). Did you mean to pass this to your component or is there a typo on its name?', $key, $key, get_debug_type($value)));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use Symfony\Component\HttpKernel\Kernel as BaseKernel;
|
||||
use Symfony\UX\TwigComponent\Tests\Fixtures\Bundle\AcmeBundle\AcmeBundle;
|
||||
use Symfony\UX\TwigComponent\Tests\Fixtures\Component\ComponentB;
|
||||
use Symfony\UX\TwigComponent\TwigComponentBundle;
|
||||
use Twig\Extra\TwigExtraBundle\TwigExtraBundle;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
@@ -33,6 +34,7 @@ final class Kernel extends BaseKernel
|
||||
yield new FrameworkBundle();
|
||||
yield new TwigBundle();
|
||||
yield new TwigComponentBundle();
|
||||
yield new TwigExtraBundle();
|
||||
yield new AcmeBundle();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{%- set dialog_trigger_attrs = {
|
||||
'data-action': 'click->dialog#open'|html_attr_type('sst'),
|
||||
'data-no-html-attr-type': 'dialog',
|
||||
'data-html-attr-type-cst': 'dialog'|html_attr_type('cst'),
|
||||
} -%}
|
||||
{%- set tooltip_trigger_attrs = {
|
||||
'data-action': 'mouseenter->tooltip#show mouseleave->tooltip#hide focus->tooltip#show blur->tooltip#hide'|html_attr_type('sst'),
|
||||
'data-no-html-attr-type': 'trigger',
|
||||
'data-html-attr-type-cst': 'trigger'|html_attr_type('cst'),
|
||||
} -%}
|
||||
|
||||
<twig:Button
|
||||
{{ ...{}|html_attr_merge(dialog_trigger_attrs, tooltip_trigger_attrs) }}
|
||||
label="A beautiful button"
|
||||
/>
|
||||
@@ -16,6 +16,7 @@ use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\UX\TwigComponent\Tests\Fixtures\User;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Extra\Html\HtmlAttr\AttributeValueInterface;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
@@ -624,6 +625,21 @@ final class ComponentExtensionTest extends KernelTestCase
|
||||
$this->assertStringContainsString('data_foo-var-defined=no', $output);
|
||||
}
|
||||
|
||||
public function testPropsWithHtmlAttrMergeFilter()
|
||||
{
|
||||
if (!interface_exists(AttributeValueInterface::class)) {
|
||||
$this->markTestSkipped('Test requires Twig HTML extra >= 3.24.');
|
||||
}
|
||||
|
||||
$output = self::getContainer()->get(Environment::class)->render('html_attr_merge.html.twig');
|
||||
|
||||
$this->assertStringContainsString('class="primary"', $output);
|
||||
$this->assertStringContainsString('data-action="click->dialog#open mouseenter->tooltip#show mouseleave->tooltip#hide focus->tooltip#show blur->tooltip#hide"', $output);
|
||||
// When no HTML Attr Type has been defined, the very last takes precedence
|
||||
$this->assertStringContainsString('data-no-html-attr-type="trigger"', $output);
|
||||
$this->assertStringContainsString('data-html-attr-type-cst="dialog, trigger"', $output);
|
||||
}
|
||||
|
||||
private function renderComponent(string $name, array $data = []): string
|
||||
{
|
||||
return self::getContainer()->get(Environment::class)->render('render_component.html.twig', [
|
||||
|
||||
Reference in New Issue
Block a user