Skip to content

Commit ddb8ecb

Browse files
committed
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 twigphp/Twig#4790, replace twigphp/Twig#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 ------- bd2410f [TwigComponent] Add support for `AttributeValueInterface` from `twig/html-extra:^3.24.0` in `ComponentAttributes`
2 parents 476c007 + bd2410f commit ddb8ecb

6 files changed

Lines changed: 45 additions & 1 deletion

File tree

‎src/TwigComponent/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.35
4+
5+
Add support for `AttributeValueInterface` from `twig/html-extra:^3.24.0` in `ComponentAttributes`
6+
37
## 2.33
48

59
- Extended support for the `index.html.twig` template fallback when resolving namespaced anonymous components

‎src/TwigComponent/composer.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
"symfony/phpunit-bridge": "^6.0|^7.0|^8.0",
4242
"symfony/stimulus-bundle": "^2.9.1",
4343
"symfony/twig-bundle": "^5.4|^6.0|^7.0|^8.0",
44-
"symfony/webpack-encore-bundle": "^1.15|^2.3.0"
44+
"symfony/webpack-encore-bundle": "^1.15|^2.3.0",
45+
"twig/extra-bundle": "^3.10.3",
46+
"twig/html-extra": "^3.10.3"
4547
},
4648
"conflict": {
4749
"symfony/config": "<5.4.0"

‎src/TwigComponent/src/ComponentAttributes.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\UX\StimulusBundle\Dto\StimulusAttributes;
1515
use Symfony\WebpackEncoreBundle\Dto\AbstractStimulusDto;
16+
use Twig\Extra\Html\HtmlAttr\AttributeValueInterface;
1617
use Twig\Runtime\EscaperRuntime;
1718

1819
/**
@@ -65,6 +66,10 @@ public function __toString(): string
6566
$value = true;
6667
}
6768

69+
if ($value instanceof AttributeValueInterface) {
70+
$value = $value->getValue();
71+
}
72+
6873
if (!\is_scalar($value) && !($value instanceof \Stringable)) {
6974
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)));
7075
}

‎src/TwigComponent/tests/Fixtures/Kernel.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\UX\TwigComponent\Tests\Fixtures\Bundle\AcmeBundle\AcmeBundle;
2121
use Symfony\UX\TwigComponent\Tests\Fixtures\Component\ComponentB;
2222
use Symfony\UX\TwigComponent\TwigComponentBundle;
23+
use Twig\Extra\TwigExtraBundle\TwigExtraBundle;
2324

2425
/**
2526
* @author Kevin Bond <kevinbond@gmail.com>
@@ -33,6 +34,7 @@ public function registerBundles(): iterable
3334
yield new FrameworkBundle();
3435
yield new TwigBundle();
3536
yield new TwigComponentBundle();
37+
yield new TwigExtraBundle();
3638
yield new AcmeBundle();
3739
}
3840

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{%- set dialog_trigger_attrs = {
2+
'data-action': 'click->dialog#open'|html_attr_type('sst'),
3+
'data-no-html-attr-type': 'dialog',
4+
'data-html-attr-type-cst': 'dialog'|html_attr_type('cst'),
5+
} -%}
6+
{%- set tooltip_trigger_attrs = {
7+
'data-action': 'mouseenter->tooltip#show mouseleave->tooltip#hide focus->tooltip#show blur->tooltip#hide'|html_attr_type('sst'),
8+
'data-no-html-attr-type': 'trigger',
9+
'data-html-attr-type-cst': 'trigger'|html_attr_type('cst'),
10+
} -%}
11+
12+
<twig:Button
13+
{{ ...{}|html_attr_merge(dialog_trigger_attrs, tooltip_trigger_attrs) }}
14+
label="A beautiful button"
15+
/>

‎src/TwigComponent/tests/Integration/ComponentExtensionTest.php‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\UX\TwigComponent\Tests\Fixtures\User;
1717
use Twig\Environment;
1818
use Twig\Error\RuntimeError;
19+
use Twig\Extra\Html\HtmlAttr\AttributeValueInterface;
1920

2021
/**
2122
* @author Kevin Bond <kevinbond@gmail.com>
@@ -624,6 +625,21 @@ public function testAttributesDoNotLeakToTemplateContext()
624625
$this->assertStringContainsString('data_foo-var-defined=no', $output);
625626
}
626627

628+
public function testPropsWithHtmlAttrMergeFilter()
629+
{
630+
if (!interface_exists(AttributeValueInterface::class)) {
631+
$this->markTestSkipped('Test requires Twig HTML extra >= 3.24.');
632+
}
633+
634+
$output = self::getContainer()->get(Environment::class)->render('html_attr_merge.html.twig');
635+
636+
$this->assertStringContainsString('class="primary"', $output);
637+
$this->assertStringContainsString('data-action="click-&gt;dialog#open mouseenter-&gt;tooltip#show mouseleave-&gt;tooltip#hide focus-&gt;tooltip#show blur-&gt;tooltip#hide"', $output);
638+
// When no HTML Attr Type has been defined, the very last takes precedence
639+
$this->assertStringContainsString('data-no-html-attr-type="trigger"', $output);
640+
$this->assertStringContainsString('data-html-attr-type-cst="dialog, trigger"', $output);
641+
}
642+
627643
private function renderComponent(string $name, array $data = []): string
628644
{
629645
return self::getContainer()->get(Environment::class)->render('render_component.html.twig', [

0 commit comments

Comments
 (0)