Skip to content

Commit

Permalink
Merge pull request #17 from Stillat/name-attributes-on-slots
Browse files Browse the repository at this point in the history
Adds support for name attributes in named slots
  • Loading branch information
JohnathonKoster authored May 13, 2023
2 parents 67e0ca4 + c9b67a0 commit bac16bf
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 2 deletions.
11 changes: 9 additions & 2 deletions src/Compiler/ComponentNodeCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,27 @@ protected function compileSlotComponent(ComponentNode $componentNode): string
}

$attributes = $this->toAttributeArray($componentNode);
unset($attributes['name']);

if (! Str::contains($componentNode->name, ':')) {
unset($attributes['name']);
}

return " @slot({$name}, null, [".$this->attributesToString($attributes).']) ';
}

protected function getSlotName(ComponentNode $componentNode): string|ParameterNode
{
if (Str::contains($componentNode->name, ':')) {
return Str::after($componentNode->name, ':');
}

$name = $componentNode->getParameter('name');

if ($name != null) {
return $name;
}

return Str::after($componentNode->name, ':');
return '';
}

protected function compileSelfClosingTag(ComponentNode $component): string
Expand Down
33 changes: 33 additions & 0 deletions src/Nodes/Components/ComponentNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,39 @@ public function getDynamicParameterContent(): string
})->implode(' ');
}

/**
* Returns true if the component node represents a slot component.
*/
public function isSlot(): bool
{
return $this->tagName === 'slot';
}

/**
* Returns the component's tag name.
*/
public function getTagName(): string
{
return $this->tagName;
}

public function getName(): string|ParameterNode
{
if (! $this->isSlot()) {
return $this->name;
}

if (Str::contains($this->name, ':')) {
return Str::after($this->name, ':');
}

if ($this->hasParameter('name')) {
return $this->getParameter('name');
}

return '';
}

public function clone(): ComponentNode
{
$component = new ComponentNode();
Expand Down
108 changes: 108 additions & 0 deletions tests/Compiler/BladeComponentTagCompilerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,106 @@ public function compile(ComponentNode $component): ?string
$this->assertSame($expected, $result);
}

public function testNameAttributeCanBeUsedIfUsingShortSlotNames()
{
$blade = <<<'EOT'
<x-input-with-slot>
<x-slot:input name="my_form_field" class="text-input-lg" data-test="data">Test</x-slot:input>
</x-input-with-slot>
EOT;

$expected = <<<'EXP'
##BEGIN-COMPONENT-CLASS##@component('Stillat\BladeParser\Tests\Compiler\InputWithSlot', 'input-with-slot', [])
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag && $constructor = (new ReflectionClass(Stillat\BladeParser\Tests\Compiler\InputWithSlot::class))->getConstructor()): ?>
<?php $attributes = $attributes->except(collect($constructor->getParameters())->map->getName()->all()); ?>
<?php endif; ?>
<?php $component->withAttributes([]); ?>
@slot('input', null, ['name' => 'my_form_field','class' => 'text-input-lg','data-test' => 'data']) Test @endslot
@endComponentClass##END-COMPONENT-CLASS##
EXP;

$result = $this->compiler([
'input-with-slot' => InputWithSlot::class,
])->compile($blade);

$this->assertSame($expected, $result);
}

public function testNameAttributeCantBeUsedIfNotUsingShortSlotNames()
{
$blade = <<<'EOT'
<x-input-with-slot>
<x-slot name="input" class="text-input-lg" data-test="data">Test</x-slot>
</x-input-with-slot>
EOT;

$expected = <<<'EXP'
##BEGIN-COMPONENT-CLASS##@component('Stillat\BladeParser\Tests\Compiler\InputWithSlot', 'input-with-slot', [])
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag && $constructor = (new ReflectionClass(Stillat\BladeParser\Tests\Compiler\InputWithSlot::class))->getConstructor()): ?>
<?php $attributes = $attributes->except(collect($constructor->getParameters())->map->getName()->all()); ?>
<?php endif; ?>
<?php $component->withAttributes([]); ?>
@slot('input', null, ['class' => 'text-input-lg','data-test' => 'data']) Test @endslot
@endComponentClass##END-COMPONENT-CLASS##
EXP;

$result = $this->compiler([
'input-with-slot' => InputWithSlot::class,
])->compile($blade);

$this->assertSame($expected, $result);
}

public function testBoundNameAttributeCanBeUsedIfUsingShortSlotNames()
{
$blade = <<<'EOT'
<x-input-with-slot>
<x-slot:input :name="'my_form_field'" class="text-input-lg" data-test="data">Test</x-slot:input>
</x-input-with-slot>
EOT;

$expected = <<<'EXP'
##BEGIN-COMPONENT-CLASS##@component('Stillat\BladeParser\Tests\Compiler\InputWithSlot', 'input-with-slot', [])
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag && $constructor = (new ReflectionClass(Stillat\BladeParser\Tests\Compiler\InputWithSlot::class))->getConstructor()): ?>
<?php $attributes = $attributes->except(collect($constructor->getParameters())->map->getName()->all()); ?>
<?php endif; ?>
<?php $component->withAttributes([]); ?>
@slot('input', null, ['name' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('my_form_field'),'class' => 'text-input-lg','data-test' => 'data']) Test @endslot
@endComponentClass##END-COMPONENT-CLASS##
EXP;

$result = $this->compiler([
'input-with-slot' => InputWithSlot::class,
])->compile($blade);

$this->assertSame($expected, $result);
}

public function testBoundNameAttributeCanBeUsedIfUsingShortSlotNamesAndNotFirstAttribute()
{
$blade = <<<'EOT'
<x-input-with-slot>
<x-slot:input class="text-input-lg" :name="'my_form_field'" data-test="data">Test</x-slot:input>
</x-input-with-slot>
EOT;

$expected = <<<'EXP'
##BEGIN-COMPONENT-CLASS##@component('Stillat\BladeParser\Tests\Compiler\InputWithSlot', 'input-with-slot', [])
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag && $constructor = (new ReflectionClass(Stillat\BladeParser\Tests\Compiler\InputWithSlot::class))->getConstructor()): ?>
<?php $attributes = $attributes->except(collect($constructor->getParameters())->map->getName()->all()); ?>
<?php endif; ?>
<?php $component->withAttributes([]); ?>
@slot('input', null, ['class' => 'text-input-lg','name' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('my_form_field'),'data-test' => 'data']) Test @endslot
@endComponentClass##END-COMPONENT-CLASS##
EXP;

$result = $this->compiler([
'input-with-slot' => InputWithSlot::class,
])->compile($blade);

$this->assertSame($expected, $result);
}

protected function mockViewFactory($existsSucceeds = true)
{
$container = new Container;
Expand Down Expand Up @@ -998,3 +1098,11 @@ public function render()
return 'profile';
}
}

class InputWithSlot extends Component
{
public function render()
{
return 'input';
}
}
27 changes: 27 additions & 0 deletions tests/Reflection/ComponentReflectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,31 @@ public function testComponentHasParameter()
$this->assertFalse($component->hasParameter('some_parameter'));
$this->assertTrue($component->hasParameter('message'));
}

public function testComponentSlotInformation()
{
$template = <<<'BLADE'
<x-input-with-slot>
<x-slot:input class="text-input-lg" :name="'my_form_field'" data-test="data">Test</x-slot:input>
</x-input-with-slot>
BLADE;

$slot = $this->getDocument($template)->findComponentByTagName('slot');

$this->assertTrue($slot->isSlot());
$this->assertSame('slot', $slot->getTagName());
$this->assertSame('input', $slot->getName());

$template = <<<'BLADE'
<x-input-with-slot>
<x-slot :name="'my_form_field'" class="text-input-lg" :name="'my_form_field'" data-test="data">Test</x-slot>
</x-input-with-slot>
BLADE;

$slot = $this->getDocument($template)->findComponentByTagName('slot');

$this->assertTrue($slot->isSlot());
$this->assertSame('slot', $slot->getTagName());
$this->assertSame("'my_form_field'", $slot->getName()->value);
}
}

0 comments on commit bac16bf

Please sign in to comment.