Skip to content

Commit

Permalink
[TwigComponent][LiveComponent] Fix DataModelPropsSubscriber for embed…
Browse files Browse the repository at this point in the history
…ded components
  • Loading branch information
sneakyvv committed Sep 5, 2023
1 parent 9265573 commit 24338fb
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 15 deletions.
22 changes: 22 additions & 0 deletions src/LiveComponent/tests/Fixtures/Component/InputComponent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component;

use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;

/**
* @author Bart Vanderstukken <[email protected]>
*/
#[AsTwigComponent('input_component')]
final class InputComponent
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\DefaultActionTrait;

/**
* @author Bart Vanderstukken <[email protected]>
*/
#[AsLiveComponent('parent_component_data_model')]
final class ParentComponentDataModel
{
use DefaultActionTrait;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;

/**
* @author Bart Vanderstukken <[email protected]>
*/
#[AsLiveComponent('parent_component_data_model_2')]
final class ParentComponentDataModel2
{
use DefaultActionTrait;

#[LiveProp(writable: true)] public string $content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<input{{ attributes }} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% component parent_component_data_model_2 with { content: 'default content on mount' } %}
{% endcomponent %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{{ component('textarea_component', { dataModel: 'content' }) }}
{% component input_component with { dataModel: 'content' } %}{% endcomponent %}
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\UX\LiveComponent\Tests\LiveComponentTestHelper;
use Symfony\UX\TwigComponent\ComponentRenderer;

final class DataModelPropsSubscriberTest extends KernelTestCase
{
use LiveComponentTestHelper;

public function testDataModelPropsAreSharedToChild(): void
{
// work around so that a session is available so CSRF doesn't fail
$session = self::getContainer()->get('session.factory')->createSession();
$request = Request::create('/');
$request->setSession($session);
$requestStack = self::getContainer()->get('request_stack');
$requestStack->push($request);
$this->fakeSession();

/** @var ComponentRenderer $renderer */
$renderer = self::getContainer()->get('ux.twig_component.component_renderer');
Expand All @@ -42,4 +40,33 @@ public function testDataModelPropsAreSharedToChild(): void
$this->assertStringContainsString('<textarea data-model="content:value">Hello data-model!</textarea>', $html);
$this->assertStringContainsString('<textarea data-model="content2:value">Value for second child</textarea>', $html);
}

public function testDataModelPropsAreAvailableInEmbeddedComponents(): void
{
$this->fakeSession();

$templateName = 'components/parent_component_data_model.html.twig';
$obscuredName = '684c45bf85d3461dbe587407892e59d8';
$this->addTemplateMap($obscuredName, $templateName);

/** @var ComponentRenderer $renderer */
$renderer = self::getContainer()->get('ux.twig_component.component_renderer');

$html = $renderer->createAndRender('parent_component_data_model', [
'attributes' => ['data-live-id' => 'dummy-live-id'],
]);

$this->assertStringContainsString('<textarea data-model="content">default content on mount</textarea>', $html);
$this->assertStringContainsString('<input data-model="content" value="default content on mount" />', $html);
}

private function fakeSession(): void
{
// work around so that a session is available so CSRF doesn't fail
$session = self::getContainer()->get('session.factory')->createSession();
$request = Request::create('/');
$request->setSession($session);
$requestStack = self::getContainer()->get('request_stack');
$requestStack->push($request);
}
}
19 changes: 10 additions & 9 deletions src/TwigComponent/src/ComponentRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,18 @@ public function embeddedContext(string $name, array $props, array $context, stri

$this->componentStack->push($mounted);

try {
$embeddedContext = $this->preRender($mounted, $context)->getVariables();

if (!isset($embeddedContext['outerBlocks'])) {
$embeddedContext['outerBlocks'] = new BlockStack();
}
$embeddedContext = $this->preRender($mounted, $context)->getVariables();

return $embeddedContext;
} finally {
$this->componentStack->pop();
if (!isset($embeddedContext['outerBlocks'])) {
$embeddedContext['outerBlocks'] = new BlockStack();
}

return $embeddedContext;
}

public function finishEmbeddedComponentRender(): void
{
$this->componentStack->pop();
}

private function preRender(MountedComponent $mounted, array $context = []): PreRenderEvent
Expand Down
9 changes: 9 additions & 0 deletions src/TwigComponent/src/Twig/ComponentExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ public function embeddedContext(string $name, array $props, array $context, stri
}
}

public function finishEmbeddedComponentRender(): void
{
try {
$this->container->get(ComponentRenderer::class)->finishEmbeddedComponentRender();
} catch (\Throwable $e) {
$this->throwRuntimeError($name, $e);
}
}

private function throwRuntimeError(string $name, \Throwable $e): void
{
if (!($e instanceof \Exception)) {
Expand Down
6 changes: 6 additions & 0 deletions src/TwigComponent/src/Twig/ComponentNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ public function compile(Compiler $compiler): void
$compiler->raw('->display($embeddedContext, $embeddedBlocks);');
$compiler->raw("\n");

$compiler->write('$this->extensions[')
->string(ComponentExtension::class)
->raw(']->finishEmbeddedComponentRender()')
->raw(";\n")
;

$compiler
->outdent()
->write('}')
Expand Down

0 comments on commit 24338fb

Please sign in to comment.