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
16 changes: 16 additions & 0 deletions packages/support/config/filament.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@

'assets_path' => null,

/*
|--------------------------------------------------------------------------
| Script Attributes
|--------------------------------------------------------------------------
| This is an array of attributes that will be added to all script tags
| rendered by Filament. You can use this to add attributes like `data-cfasync` or `async` to
| all scripts, or to add custom data attributes that your application may require.
|
*/

'scripts' => [
'attributes' => [
// 'data-cfasync' => 'false',
],
],

/*
|--------------------------------------------------------------------------
| Cache Path
Expand Down
8 changes: 7 additions & 1 deletion packages/support/resources/views/assets.blade.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
@if (isset($data))
<script>
<script @foreach ($dataScriptAttributes ?? [] as $key => $value)
@if (is_int($key))
{{ $value }}
@else
{{ $key }}="{{ $value }}"
@endif
@endforeach>
window.filamentData = @js($data)
</script>
@endif
Expand Down
11 changes: 10 additions & 1 deletion packages/support/src/Assets/AssetManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,9 @@ public function getScripts(?array $packages = null, bool $withCore = true): arra

/**
* @param array<string> | null $packages
* @param array<int|string, string> $attributes
*/
public function renderScripts(?array $packages = null, bool $withCore = false): string
public function renderScripts(?array $packages = null, bool $withCore = false, array $attributes = []): string
{
/** @var array<Js> $assets */
$assets = $this->getScripts($packages, $withCore);
Expand All @@ -201,9 +202,17 @@ public function renderScripts(?array $packages = null, bool $withCore = false):
);
}

$globalAttributes = config('filament.scripts.attributes', []);
$mergedAttributes = array_merge($globalAttributes, $attributes);

foreach ($assets as $asset) {
$asset->extraAttributes($mergedAttributes);
}

return view('filament::assets', [
'assets' => $assets,
'data' => $this->getScriptData($packages),
'dataScriptAttributes' => $mergedAttributes,
])->render();
}

Expand Down
10 changes: 8 additions & 2 deletions packages/support/src/Assets/Js.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,16 @@ public function getExtraAttributes(): array

public function getExtraAttributesHtml(): string
{
/** @var array<int|string, string> $extraAttributes */
$extraAttributes = $this->getExtraAttributes();
$attributes = '';

foreach ($this->getExtraAttributes() as $key => $value) {
$attributes .= " {$key}=\"{$value}\"";
foreach ($extraAttributes as $key => $value) {
if (is_int($key)) {
$attributes .= " {$value}";
} else {
$attributes .= " {$key}=\"{$value}\"";
}
}

return $attributes;
Expand Down
120 changes: 120 additions & 0 deletions tests/src/Support/BladeDirectives/FilamentScriptsAttributesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

use Filament\Tests\TestCase;
use Illuminate\Support\Facades\Blade;

uses(TestCase::class);

/*
/ Default behavior tests
*/
it('renders script tags without extra attributes', function (): void {
$html = Blade::render('@filamentScripts');

preg_match_all('/<script[\s\S]*?>/m', $html, $matches);

expect($matches[0])->not->toBeEmpty()
->and($html)->not->toContain('data-cfasync');
});

/*
/ Inline attributes tests
*/

it('applies inline attributes to every rendered script tag', function (): void {
$html = Blade::render("@filamentScripts(attributes: ['data-cfasync' => 'false'])");
$htmlWithout = Blade::render('@filamentScripts');

preg_match_all('/<script[\s\S]*?src=[\s\S]*?>/m', $html, $withMatches);
preg_match_all('/<script[\s\S]*?src=[\s\S]*?>/m', $htmlWithout, $withoutMatches);

expect($withMatches[0])->toHaveCount(count($withoutMatches[0]));

foreach ($withMatches[0] as $tag) {
expect($tag)->toContain('data-cfasync="false"');
}
});

/*
/ Keyed vs non-keyed attributes tests
*/

it('renders integer-keyed attributes as normal attributes, not integer keys like 0="async"', function (): void {
$html = Blade::render("@filamentScripts(attributes: ['data-cfasync' => 'false', 'async'])");

expect($html)
->toContain('data-cfasync="false"')
->toContain(' async')
->not->toContain('0="async"')
->not->toContain('="async"');
});

/*
/ FilamentData script block tests
*/

it('applies inline attributes to the filamentData script block', function (): void {
$html = Blade::render("@filamentScripts(attributes: ['data-cfasync' => 'false'])");

expect($html)
->toMatch('/<script[^>]*data-cfasync="false"[^>]*>\s*window\.filamentData/');
});

it('applies global config attributes to the filamentData script block', function (): void {
config(['filament.scripts.attributes' => ['data-cfasync' => 'false']]);

$html = Blade::render('@filamentScripts');

expect($html)
->toMatch('/<script[^>]*data-cfasync="false"[^>]*>\s*window\.filamentData/');
});

it('applies inline attributes to both the filamentData block and all src script tags', function (): void {
$html = Blade::render("@filamentScripts(attributes: ['data-cfasync' => 'false'])");

preg_match_all('/<script[\s\S]*?data-cfasync="false"[\s\S]*?>/m', $html, $matches);

expect(count($matches[0]))->toBeGreaterThanOrEqual(2);
});

/*
/ Global config attributes tests
*/

it('global config attributes apply to every script tag', function (): void {
config(['filament.scripts.attributes' => ['data-cfasync' => 'false']]);

$html = Blade::render('@filamentScripts');

preg_match_all('/<script[\s\S]*?src=[\s\S]*?>/m', $html, $matches);

expect($matches[0])->not->toBeEmpty();

foreach ($matches[0] as $tag) {
expect($tag)->toContain('data-cfasync="false"');
}
});

/*
/ Overriding and merging attributes tests
*/

it('inline attributes override global config attributes for the same key', function (): void {
config(['filament.scripts.attributes' => ['data-cfasync' => 'false']]);

$html = Blade::render("@filamentScripts(attributes: ['data-cfasync' => 'true'])");

expect($html)
->toContain('data-cfasync="true"')
->not->toContain('data-cfasync="false"');
});

it('merges global config attributes with inline attributes', function (): void {
config(['filament.scripts.attributes' => ['data-cfasync' => 'false']]);

$html = Blade::render("@filamentScripts(attributes: ['crossorigin' => 'anonymous'])");

expect($html)
->toContain('data-cfasync="false"')
->toContain('crossorigin="anonymous"');
});