Skip to content

Commit 2e17b4d

Browse files
committed
[Live] Fixing a bug so that data-live-id is not overridden for child components
1 parent 0e22b90 commit 2e17b4d

File tree

6 files changed

+58
-21
lines changed

6 files changed

+58
-21
lines changed

src/LiveComponent/src/EventListener/AddLiveAttributesSubscriber.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,10 @@ private function getLiveAttributes(MountedComponent $mounted, ComponentMetadata
112112
}
113113

114114
if ($this->container->get(ComponentStack::class)->hasParentComponent()) {
115-
$id = $this->container->get(DeterministicTwigIdCalculator::class)->calculateDeterministicId();
116-
$attributes['data-live-id'] = $helper->escapeAttribute($id);
115+
if (!isset($mounted->getAttributes()->all()['data-live-id'])) {
116+
$id = $this->container->get(DeterministicTwigIdCalculator::class)->calculateDeterministicId();
117+
$attributes['data-live-id'] = $helper->escapeAttribute($id);
118+
}
117119

118120
$fingerprint = $this->container->get(FingerprintCalculator::class)->calculateFingerprint($mounted->getInputProps());
119121
$attributes['data-live-value-fingerprint'] = $helper->escapeAttribute($fingerprint);

src/LiveComponent/tests/Fixtures/Component/TodoListComponent.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@ final class TodoListComponent
2424
#[LiveProp]
2525
public array $items = [];
2626

27+
public $includeDataLiveId = false;
28+
2729
use DefaultActionTrait;
2830
}

src/LiveComponent/tests/Fixtures/templates/components/todo_list.html.twig

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33

44
<ul>
55
{% for item in items %}
6-
{{ component('todo_item', {
7-
text: item.text
8-
}) }}
6+
{% set componentProps = { text: item.text } %}
7+
{% if includeDataLiveId %}
8+
{% set componentProps = componentProps|merge({'data-live-id': ('todo-item-' ~ loop.index) }) %}
9+
{% endif %}
10+
{{ component('todo_item', componentProps) }}
911
{% endfor %}
1012
</ul>
1113
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{ component('todo_list', {
2+
items: [
3+
{ text: 'milk'},
4+
{ text: 'cheese'},
5+
],
6+
includeDataLiveId: true
7+
}) }}

src/LiveComponent/tests/Functional/EventListener/AddLiveAttributesSubscriberTest.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
*/
2020
final class AddLiveAttributesSubscriberTest extends KernelTestCase
2121
{
22+
/**
23+
* The deterministic id of the "todo_item" components in todo_list.html.twig.
24+
* If that template changes, this will need to be updated.
25+
*/
26+
public const TODO_ITEM_DETERMINISTIC_PREFIX = 'live-289310975-';
27+
2228
use HasBrowser;
2329

2430
public function testInitLiveComponent(): void
@@ -85,8 +91,8 @@ public function testItAddsIdAndFingerprintToChildComponent(): void
8591

8692
$lis = $ul->children('li');
8793
// deterministic id: should not change, and counter should increase
88-
$this->assertSame('live-3649730296-0', $lis->first()->attr('data-live-id'));
89-
$this->assertSame('live-3649730296-2', $lis->last()->attr('data-live-id'));
94+
$this->assertSame(self::TODO_ITEM_DETERMINISTIC_PREFIX . '0', $lis->first()->attr('data-live-id'));
95+
$this->assertSame(self::TODO_ITEM_DETERMINISTIC_PREFIX . '2', $lis->last()->attr('data-live-id'));
9096

9197
// fingerprints
9298
// first and last both have the same input - thus fingerprint
@@ -95,4 +101,19 @@ public function testItAddsIdAndFingerprintToChildComponent(): void
95101
// middle has a different fingerprint
96102
$this->assertSame('cuOKkrHC9lOmBa6dyVZ3S0REdw4CKCwJgLDdrVoTb2g=', $lis->eq(1)->attr('data-live-value-fingerprint'));
97103
}
104+
105+
public function testItDoesNotOverrideDataLiveIdIfSpecified(): void
106+
{
107+
$ul = $this->browser()
108+
->visit('/render-template/render_todo_list_with_live_id')
109+
->assertSuccessful()
110+
->crawler()
111+
->filter('ul')
112+
;
113+
114+
$lis = $ul->children('li');
115+
// deterministic id: is not used: data-live-id was passed in manually
116+
$this->assertSame('todo-item-1', $lis->first()->attr('data-live-id'));
117+
$this->assertSame('todo-item-2', $lis->last()->attr('data-live-id'));
118+
}
98119
}

src/LiveComponent/tests/Functional/EventListener/InterceptChildComponentRenderSubscriberTest.php

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ final class InterceptChildComponentRenderSubscriberTest extends KernelTestCase
2525
// if you pass in 3 "items" with data that matches what's used by default
2626
// in buildUrlForTodoListComponent
2727
private static array $actualTodoItemFingerprints = [
28-
'live-3649730296-0' => 'LwqODySoRx3q+v64EzalGouzpSHWKIm0jENTUGtQloE=',
29-
'live-3649730296-1' => 'gn9PcPUqL0tkeLSw0ZuhOj96dwIpiBmJPoO5NPync2o=',
30-
'live-3649730296-2' => 'ndV00y/qOSH11bjOKGDJVRsxANtbudYB6K8D46viUI8=',
28+
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX . '0' => 'LwqODySoRx3q+v64EzalGouzpSHWKIm0jENTUGtQloE=',
29+
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX . '1' => 'gn9PcPUqL0tkeLSw0ZuhOj96dwIpiBmJPoO5NPync2o=',
30+
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX . '2' => 'ndV00y/qOSH11bjOKGDJVRsxANtbudYB6K8D46viUI8=',
3131
];
3232

3333
public function testItAllowsFullChildRenderOnMissingFingerprints(): void
@@ -59,7 +59,7 @@ public function testItRendersEmptyElementOnMatchingFingerprint(): void
5959
public function testItRendersNewPropWhenFingerprintDoesNotMatch(): void
6060
{
6161
$fingerprints = self::$actualTodoItemFingerprints;
62-
$fingerprints['live-3649730296-1'] = 'wrong fingerprint';
62+
$fingerprints[AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX . '1'] = 'wrong fingerprint';
6363

6464
$this->browser()
6565
->visit($this->buildUrlForTodoListComponent($fingerprints))
@@ -74,16 +74,19 @@ public function testItRendersNewPropWhenFingerprintDoesNotMatch(): void
7474

7575
// 1st and 3rd render empty
7676
// fingerprint changed in 2nd, so it renders new fingerprint + props
77-
$this->assertStringContainsString(<<<EOF
78-
<ul>
79-
<div data-live-id="live-3649730296-0"></div>
80-
<div data-live-id="live-3649730296-1" data-live-fingerprint-value="gn9PcPUqL0tkeLSw0ZuhOj96dwIpiBmJPoO5NPync2o&#x3D;" data-live-props-value="&#x7B;&quot;_checksum&quot;&#x3A;&quot;YrPg4mHsB82fR&#x5C;&#x2F;VmTL3gJIX32kS8HvWy&#x5C;&#x2F;9uKSs&#x5C;&#x2F;aPfk&#x3D;&quot;&#x7D;"></div>
81-
<div data-live-id="live-3649730296-2"></div>
82-
</ul>
83-
EOF,
84-
$content);
85-
})
86-
;
77+
$this->assertStringContainsString(sprintf(
78+
'<div data-live-id="%s0"></div>',
79+
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX
80+
), $content);
81+
$this->assertStringContainsString(sprintf(
82+
'<div data-live-id="%s1" data-live-fingerprint-value="gn9PcPUqL0tkeLSw0ZuhOj96dwIpiBmJPoO5NPync2o&#x3D;" data-live-props-value="&#x7B;&quot;_checksum&quot;&#x3A;&quot;YrPg4mHsB82fR&#x5C;&#x2F;VmTL3gJIX32kS8HvWy&#x5C;&#x2F;9uKSs&#x5C;&#x2F;aPfk&#x3D;&quot;&#x7D;"></div>',
83+
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX
84+
), $content);
85+
$this->assertStringContainsString(sprintf(
86+
'<div data-live-id="%s2"></div>',
87+
AddLiveAttributesSubscriberTest::TODO_ITEM_DETERMINISTIC_PREFIX
88+
), $content);
89+
});
8790
}
8891

8992
private function buildUrlForTodoListComponent(array $childrenFingerprints): string

0 commit comments

Comments
 (0)