Skip to content

Commit

Permalink
[8.x] Fix extra space on blade class components that are inline (#35874)
Browse files Browse the repository at this point in the history
This fixes extra spacing before and after Blade class components.
  • Loading branch information
taylorotwell committed Jan 14, 2021
1 parent 67451f9 commit 83ec510
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 43 deletions.
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

0 comments on commit 83ec510

Please sign in to comment.