Skip to content

Commit

Permalink
[9.x] Allow @class() for component tags (#43140)
Browse files Browse the repository at this point in the history
* Allow class directive inside tag components

* formatting

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
aguingand and taylorotwell committed Aug 4, 2022
1 parent 075dde1 commit fd4451e
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
35 changes: 34 additions & 1 deletion src/Illuminate/View/Compilers/ComponentTagCompiler.php
Expand Up @@ -111,6 +111,10 @@ protected function compileOpeningTags(string $value)
(?:
\s+
(?:
(?:
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
)
Expand Down Expand Up @@ -164,6 +168,10 @@ protected function compileSelfClosingTags(string $value)
(?:
\s+
(?:
(?:
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
)
Expand Down Expand Up @@ -432,6 +440,10 @@ public function compileSlots(string $value)
(?:
\s+
(?:
(?:
@(?:class)(\( (?: (?>[^()]+) | (?-1) )* \))
)
|
(?:
\{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\}
)
Expand Down Expand Up @@ -487,7 +499,7 @@ public function compileSlots(string $value)
protected function getAttributesFromAttributeString(string $attributeString)
{
$attributeString = $this->parseAttributeBag($attributeString);

$attributeString = $this->parseComponentTagClassStatements($attributeString);
$attributeString = $this->parseBindAttributes($attributeString);

$pattern = '/
Expand Down Expand Up @@ -554,6 +566,27 @@ protected function parseAttributeBag(string $attributeString)
return preg_replace($pattern, ' :attributes="$1"', $attributeString);
}

/**
* Parse @class statements in a given attribute string into their fully-qualified syntax.
*
* @param string $attributeString
* @return string
*/
protected function parseComponentTagClassStatements(string $attributeString)
{
return preg_replace_callback(
'/@(class)(\( ( (?>[^()]+) | (?2) )* \))/x', function ($match) {
if ($match[1] === 'class') {
$match[2] = str_replace('"', "'", $match[2]);

return ":class=\"\Illuminate\Support\Arr::toCssClasses{$match[2]}\"";
}

return $match[0];
}, $attributeString
);
}

/**
* Parse the "bind" attributes in a given attribute string into their fully-qualified syntax.
*
Expand Down
20 changes: 20 additions & 0 deletions tests/View/Blade/BladeComponentTagCompilerTest.php
Expand Up @@ -76,6 +76,14 @@ public function testSlotsWithDynamicAttributesCanBeCompiled()
$this->assertSame("@slot('foo', null, ['class' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\$classes)]) \n".' @endslot', trim($result));
}

public function testSlotsWithClassDirectiveCanBeCompiled()
{
$result = $this->compiler()->compileSlots('<x-slot name="foo" @class($classes)>
</x-slot>');

$this->assertSame("@slot('foo', null, ['class' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\Illuminate\Support\Arr::toCssClasses(\$classes))]) \n".' @endslot', trim($result));
}

public function testBasicComponentParsing()
{
$this->mockViewFactory();
Expand Down Expand Up @@ -151,6 +159,18 @@ public function testColonAttributesIsEscapedIfStrings()
<?php \$component->withAttributes(['src' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('foo')]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

public function testClassDirective()
{
$this->mockViewFactory();
$result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags('<x-profile @class(["bar"=>true])></x-profile>');

$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', [])
<?php if (isset(\$attributes) && \$constructor = (new ReflectionClass(Illuminate\Tests\View\Blade\TestProfileComponent::class))->getConstructor()): ?>
<?php \$attributes = \$attributes->except(collect(\$constructor->getParameters())->map->getName()->all()); ?>
<?php endif; ?>
<?php \$component->withAttributes(['class' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\Illuminate\Support\Arr::toCssClasses(['bar'=>true]))]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

public function testColonNestedComponentParsing()
{
$result = $this->compiler(['foo:alert' => TestAlertComponent::class])->compileTags('<x-foo:alert></x-foo:alert>');
Expand Down

0 comments on commit fd4451e

Please sign in to comment.