Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions src/Tags/ComponentProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Statamic\Tags;

use Illuminate\Support\Str;
use Illuminate\View\AnonymousComponent;
use Illuminate\View\Compilers\BladeCompiler;
use Illuminate\View\Compilers\ComponentTagCompiler;
Expand Down Expand Up @@ -51,12 +52,22 @@ public function index()
}

if ($constructor = (new ReflectionClass($className))->getConstructor()) {
$constructorParameters = collect($constructor->getParameters())->map->getName()->all();
$attributes = $attributes->except($constructorParameters);
$constructorParameters = collect($scopeData)->only($constructorParameters)->all();
$parameterNames = collect($constructor->getParameters())->map->getName()->all();

// Kebab-cased attributes (e.g. :some-prop) should bind to camelCase
// constructor parameters ($someProp), mirroring Laravel's native behavior.
$attributes = $attributes->filter(fn ($value, $key) => ! in_array(Str::camel($key), $parameterNames));

$constructorParameters = collect($scopeData)
->mapWithKeys(fn ($value, $key) => [Str::camel($key) => $value])
->only($parameterNames)
->all();
}

if ($isAnonymous) {
// Camel-case data keys so kebab-cased attributes resolve to the
// component's @props (e.g. :some-prop -> $someProp), as Laravel does.
$data = collect($data)->mapWithKeys(fn ($value, $key) => [Str::camel($key) => $value])->all();
$constructorParameters = array_merge($constructorParameters, $data, ['view' => $anonymousViewName, 'data' => $data]);
}

Expand Down
53 changes: 53 additions & 0 deletions tests/Antlers/Components/BladeComponentsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Support\Facades\Blade;
use Statamic\View\Antlers\Language\Utilities\StringUtilities;
use Tests\Antlers\Fixtures\Components\Card;
use Tests\Antlers\Fixtures\Components\KebabProp;
use Tests\Antlers\ParserTestCase;

class BladeComponentsTest extends ParserTestCase
Expand Down Expand Up @@ -126,6 +127,58 @@ public function test_components_blade_compatibility()
);
}

public function test_kebab_cased_attributes_bind_props_on_anonymous_blade_components()
{
$template = <<<'EOT'
{{ foo = 'Test' }}<x-kebab_prop :some-prop="foo" />
EOT;

$this->assertSame(
'<div data-test="Test"></div>',
trim($this->renderString($template))
);
}

public function test_kebab_cased_attributes_bind_props_on_class_blade_components()
{
Blade::component(KebabProp::class);

$template = <<<'EOT'
{{ foo = 'Test' }}<x-kebab_prop_class :some-prop="foo" />
EOT;

$this->assertSame(
'Test',
trim($this->renderString($template))
);
}

public function test_camel_cased_attributes_still_bind_props_on_anonymous_blade_components()
{
$template = <<<'EOT'
{{ foo = 'Test' }}<x-kebab_prop :someProp="foo" />
EOT;

$this->assertSame(
'<div data-test="Test"></div>',
trim($this->renderString($template))
);
}

public function test_camel_cased_attributes_still_bind_props_on_class_blade_components()
{
Blade::component(KebabProp::class);

$template = <<<'EOT'
{{ foo = 'Test' }}<x-kebab_prop_class :someProp="foo" />
EOT;

$this->assertSame(
'Test',
trim($this->renderString($template))
);
}

public function test_method_calls_across_lines_with_attributes()
{
$template = <<<'EOT'
Expand Down
19 changes: 19 additions & 0 deletions tests/Antlers/Fixtures/Components/KebabProp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Tests\Antlers\Fixtures\Components;

use Illuminate\View\Component;

class KebabProp extends Component
{
public function __construct(
public ?string $someProp = null,
) {

}

public function render()
{
return view('components.kebab_prop_class');
}
}
2 changes: 2 additions & 0 deletions tests/__fixtures__/views/components/kebab_prop.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@props(['someProp' => null])
<div data-test="{{ $someProp }}"></div>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ someProp }}
Loading