Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] Fix extra space on blade class components that are inline #35874

Merged
merged 7 commits into from Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/Illuminate/View/Compilers/BladeCompiler.php
Expand Up @@ -251,7 +251,10 @@ public function compileString($value)
$result = $this->addFooters($result);
}

return $result;
return str_replace(
['##BEGIN-COMPONENT-CLASS##', '##END-COMPONENT-CLASS##'],
'',
$result);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/Illuminate/View/Compilers/ComponentTagCompiler.php
Expand Up @@ -193,7 +193,7 @@ protected function compileSelfClosingTags(string $value)

$attributes = $this->getAttributesFromAttributeString($matches['attributes']);

return $this->componentString($matches[1], $attributes)."\n@endcomponentClass ";
return $this->componentString($matches[1], $attributes)."\n@endComponentClass##END-COMPONENT-CLASS##";
}, $value);
}

Expand Down Expand Up @@ -230,7 +230,7 @@ protected function componentString(string $component, array $attributes)
$parameters = $data->all();
}

return " @component('{$class}', '{$component}', [".$this->attributesToString($parameters, $escapeBound = false).'])
return "##BEGIN-COMPONENT-CLASS##@component('{$class}', '{$component}', [".$this->attributesToString($parameters, $escapeBound = false).'])
<?php $component->withAttributes(['.$this->attributesToString($attributes->all(), $escapeAttributes = $class !== DynamicComponent::class).']); ?>';
}

Expand Down Expand Up @@ -384,7 +384,7 @@ public function partitionDataAndAttributes($class, array $attributes)
*/
protected function compileClosingTags(string $value)
{
return preg_replace("/<\/\s*x[-\:][\w\-\:\.]*\s*>/", ' @endcomponentClass ', $value);
return preg_replace("/<\/\s*x[-\:][\w\-\:\.]*\s*>/", ' @endComponentClass##END-COMPONENT-CLASS##', $value);
}

/**
Expand Down
10 changes: 8 additions & 2 deletions tests/Integration/View/BladeTest.php
Expand Up @@ -42,12 +42,18 @@ public function test_rendering_the_same_dynamic_component_with_different_attribu
$this->assertSame('<span class="text-medium">
Hello Taylor
</span>

<span >
<span >
Hello Samuel
</span>', trim($view));
}

public function test_inline_link_type_attributes_dont_add_extra_spacing_at_end()
{
$view = View::make('uses-link')->render();

$this->assertSame('This is a sentence with a <a href="https://laravel.com">link</a>.', trim($view));
}

public function test_appendable_attributes()
{
$view = View::make('uses-appendable-panel', ['name' => 'Taylor', 'withInjectedValue' => true])->render();
Expand Down
3 changes: 3 additions & 0 deletions tests/Integration/View/templates/components/link.blade.php
@@ -0,0 +1,3 @@
@props(['href'])

<a href="{{ $href }}">{{ $slot }}</a>
1 change: 1 addition & 0 deletions tests/Integration/View/templates/uses-link.blade.php
@@ -0,0 +1 @@
This is a sentence with a <x-link href="https://laravel.com">link</x-link>.
74 changes: 37 additions & 37 deletions tests/View/Blade/BladeComponentTagCompilerTest.php
Expand Up @@ -40,69 +40,69 @@ public function testBasicComponentParsing()

$result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('<div><x-alert type="foo" limit="5" @click="foo" wire:click="changePlan(\'{{ $plan }}\')" required /><x-alert /></div>');

$this->assertSame("<div> @component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
$this->assertSame("<div>##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
<?php \$component->withAttributes(['type' => 'foo','limit' => '5','@click' => 'foo','wire:click' => 'changePlan(\''.e(\$plan).'\')','required' => true]); ?>\n".
"@endcomponentClass @component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
"@endComponentClass##END-COMPONENT-CLASS####BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
<?php \$component->withAttributes([]); ?>\n".
'@endcomponentClass </div>', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##</div>', trim($result));
}

public function testBasicComponentWithEmptyAttributesParsing()
{
$result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('<div><x-alert type="" limit=\'\' @click="" required /></div>');

$this->assertSame("<div> @component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
$this->assertSame("<div>##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
<?php \$component->withAttributes(['type' => '','limit' => '','@click' => '','required' => true]); ?>\n".
'@endcomponentClass </div>', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##</div>', trim($result));
}

public function testDataCamelCasing()
{
$result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags('<x-profile user-id="1"></x-profile>');

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', ['userId' => '1'])
<?php \$component->withAttributes([]); ?> @endcomponentClass", trim($result));
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', ['userId' => '1'])
<?php \$component->withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

public function testColonData()
{
$result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags('<x-profile :user-id="1"></x-profile>');

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', ['userId' => 1])
<?php \$component->withAttributes([]); ?> @endcomponentClass", trim($result));
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', ['userId' => 1])
<?php \$component->withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

public function testColonAttributesIsEscapedIfStrings()
{
$result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags('<x-profile :src="\'foo\'"></x-profile>');

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', [])
<?php \$component->withAttributes(['src' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('foo')]); ?> @endcomponentClass", trim($result));
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', [])
<?php \$component->withAttributes(['src' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('foo')]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

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

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'foo:alert', [])
<?php \$component->withAttributes([]); ?> @endcomponentClass", trim($result));
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'foo:alert', [])
<?php \$component->withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

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

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'foo:alert', [])
<?php \$component->withAttributes([]); ?> @endcomponentClass", trim($result));
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'foo:alert', [])
<?php \$component->withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

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

$this->assertSame("<div> @component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
$this->assertSame("<div>##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
<?php \$component->withAttributes([]); ?>\n".
'@endcomponentClass </div>', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##</div>', trim($result));
}

public function testClassNamesCanBeGuessed()
Expand Down Expand Up @@ -139,27 +139,27 @@ public function testComponentsCanBeCompiledWithHyphenAttributes()

$result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('<x-alert class="bar" wire:model="foo" x-on:click="bar" @click="baz" />');

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
<?php \$component->withAttributes(['class' => 'bar','wire:model' => 'foo','x-on:click' => 'bar','@click' => 'baz']); ?>\n".
'@endcomponentClass', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##', trim($result));
}

public function testSelfClosingComponentsCanBeCompiledWithDataAndAttributes()
{
$result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('<x-alert title="foo" class="bar" wire:model="foo" />');

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => 'foo'])
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => 'foo'])
<?php \$component->withAttributes(['class' => 'bar','wire:model' => 'foo']); ?>\n".
'@endcomponentClass', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##', trim($result));
}

public function testComponentCanReceiveAttributeBag()
{
$this->mockViewFactory();
$result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags('<x-profile class="bar" {{ $attributes }} wire:model="foo"></x-profile>');

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', [])
<?php \$component->withAttributes(['class' => 'bar','attributes' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\$attributes),'wire:model' => 'foo']); ?> @endcomponentClass", trim($result));
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', [])
<?php \$component->withAttributes(['class' => 'bar','attributes' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\$attributes),'wire:model' => 'foo']); ?> @endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

public function testSelfClosingComponentCanReceiveAttributeBag()
Expand All @@ -168,45 +168,45 @@ public function testSelfClosingComponentCanReceiveAttributeBag()

$result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('<div><x-alert title="foo" class="bar" {{ $attributes->merge([\'class\' => \'test\']) }} wire:model="foo" /></div>');

$this->assertSame("<div> @component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => 'foo'])
$this->assertSame("<div>##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => 'foo'])
<?php \$component->withAttributes(['class' => 'bar','attributes' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\$attributes->merge(['class' => 'test'])),'wire:model' => 'foo']); ?>\n".
'@endcomponentClass </div>', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##</div>', trim($result));
}

public function testComponentsCanHaveAttachedWord()
{
$result = $this->compiler(['profile' => TestProfileComponent::class])->compileTags('<x-profile></x-profile>Words');

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', [])
<?php \$component->withAttributes([]); ?> @endcomponentClass Words", trim($result));
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestProfileComponent', 'profile', [])
<?php \$component->withAttributes([]); ?> @endComponentClass##END-COMPONENT-CLASS##Words", trim($result));
}

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

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
<?php \$component->withAttributes([]); ?>\n".
'@endcomponentClass Words', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##Words', trim($result));
}

public function testSelfClosingComponentsCanBeCompiledWithBoundData()
{
$result = $this->compiler(['alert' => TestAlertComponent::class])->compileTags('<x-alert :title="$title" class="bar" />');

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => \$title])
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', ['title' => \$title])
<?php \$component->withAttributes(['class' => 'bar']); ?>\n".
'@endcomponentClass', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##', trim($result));
}

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

$this->assertSame("@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\Tests\View\Blade\TestAlertComponent', 'alert', [])
<?php \$component->withAttributes([]); ?>
@endcomponentClass", trim($result));
@endComponentClass##END-COMPONENT-CLASS##", trim($result));
}

public function testClasslessComponents()
Expand All @@ -220,9 +220,9 @@ public function testClasslessComponents()

$result = $this->compiler()->compileTags('<x-anonymous-component :name="\'Taylor\'" :age="31" wire:model="foo" />');

$this->assertSame("@component('Illuminate\View\AnonymousComponent', 'anonymous-component', ['view' => 'components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']])
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'anonymous-component', ['view' => 'components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']])
<?php \$component->withAttributes(['name' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('Taylor'),'age' => 31,'wire:model' => 'foo']); ?>\n".
'@endcomponentClass', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##', trim($result));
}

public function testPackagesClasslessComponents()
Expand All @@ -236,9 +236,9 @@ public function testPackagesClasslessComponents()

$result = $this->compiler()->compileTags('<x-package::anonymous-component :name="\'Taylor\'" :age="31" wire:model="foo" />');

$this->assertSame("@component('Illuminate\View\AnonymousComponent', 'package::anonymous-component', ['view' => 'package::components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']])
$this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'package::anonymous-component', ['view' => 'package::components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']])
<?php \$component->withAttributes(['name' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute('Taylor'),'age' => 31,'wire:model' => 'foo']); ?>\n".
'@endcomponentClass', trim($result));
'@endComponentClass##END-COMPONENT-CLASS##', trim($result));
}

public function testAttributeSanitization()
Expand Down