diff --git a/.gitignore b/.gitignore index 4677143..c3a9290 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ phpunit.xml phpstan.neon testbench.yaml vendor -.phpunit.cache \ No newline at end of file +.phpunit.cache +CLAUDE.md diff --git a/README.md b/README.md index c44632b..72eee3c 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ - [disabling block in select block](#disabling-block-in-select-block) - [iframe resizing](#iframe-resizing) - [Parameter injection](#parameter-injection) + - [Global blocks](#global-blocks) - [Rendering page builder items on infolist](#rendering-page-builder-items-on-infolist) - [Rendering page builder ite previews on fomrms](#rendering-page-builder-ite-previews-on-fomrms) - [Customizing actions and button rendering](#customizing-actions-and-button-rendering) @@ -540,6 +541,136 @@ injections that are provided depends on where this function is used, if this is one thing to note is that because `formatForListingView` uses `formatForSingleView` internally if you wish to inject something in `formatForSingleView` you will need to inject it in `formatForListingView` as well, otherwise it will not work. +### Global blocks + +Global blocks are special blocks that have centralized configuration management. Instead of configuring the same block repeatedly across different pages, you can set up the block configuration once and reuse it everywhere. + +#### Creating global blocks + +You can create a global block using the make-block command with the `--global` flag: + +```bash +php artisan page-builder-plugin:make-block ContactForm --type=view --global +``` + +This will: +- Create a global block class in `app/Filament/{panel}/Blocks/Globals/` directory +- Create a `Globals` block category for organization +- Show instructions for enabling the Global Blocks resource + +#### Enabling the Global Blocks resource + +To manage global blocks in your Filament admin panel, you need to register the GlobalBlocksPlugin in your panel provider: + +```php +plugins([ + \Redberry\PageBuilderPlugin\GlobalBlocksPlugin::make(), + // ... other plugins + ]); +} +``` + +This will add a "Global Blocks" resource to your admin navigation under the "Content Management" group. + +#### Configuring the Global Blocks plugin + +You can configure the plugin directly when registering it: + +```php +plugins([ + \Redberry\PageBuilderPlugin\GlobalBlocksPlugin::make() + ->enableGlobalBlocks(true) // Enable/disable the Global Blocks resource + ->resource(\App\Filament\Resources\CustomGlobalBlocksResource::class), // Use custom resource + // ... other plugins +]) +``` + +**Configuration options:** +- `enableGlobalBlocks(bool)`: Enable or disable the Global Blocks resource (default: `true`) +- `resource(string)`: Specify a custom resource class that extends the package's resource + +This approach allows you to: +- **Enable Global Blocks on specific panels only** - Perfect for multi-panel applications +- **Use different resource configurations per panel** - Each panel can have its own customized resource +- **Disable the feature entirely** by not registering the plugin or using `->enableGlobalBlocks(false)` + +#### How global blocks work + +Global blocks use the `IsGlobalBlock` trait and have two key methods: + +```php +required(), + Textarea::make('description'), + TextInput::make('email')->email(), + ]; + } + + // The schema returned to the page builder (empty for global blocks) + public static function getBlockSchema(?object $record = null): array + { + $schema = static::getBaseBlockSchema($record); + return static::applyGlobalConfiguration($schema); + } +} +``` + +#### Global Blocks resource + +The "Global Blocks" resource is provided by the package and becomes available once you register the GlobalBlocksPlugin in your panel. This resource allows you to: + +- View all available global blocks +- Configure each block's field values +- Edit configurations using the actual Filament form fields defined in `getBaseBlockSchema()` + + +#### Using global blocks on pages + +When adding global blocks to pages: +- **No configuration modal appears** - blocks are added instantly +- **No per-page configuration** - all configuration is managed centrally +- **Consistent values everywhere** - the block uses the same configured values across all pages + +Simply add your global block to the page builder's blocks array: + +```php +blocks([ + ContactForm::class, + // other blocks... + ]); +``` + +#### Key benefits + +- **Centralized management**: Configure once, use everywhere +- **Consistency**: Same content and styling across all instances +- **Efficiency**: No need to reconfigure the same block on multiple pages +- **Maintainability**: Update global block configuration in one place + ### Rendering page builder items on infolist outside of form you might want to render page builder items on infolist, for this we provide two prebuilt entries: `PageBuilderEntry` and `PageBuilderPreviewEntry` diff --git a/database/factories/GlobalBlockConfigFactory.php b/database/factories/GlobalBlockConfigFactory.php new file mode 100644 index 0000000..de71fdd --- /dev/null +++ b/database/factories/GlobalBlockConfigFactory.php @@ -0,0 +1,25 @@ + $this->faker->words(3, true), + 'class_name' => GlobalViewBlock::class, + 'configuration' => [ + 'title' => $this->faker->sentence, + 'content' => $this->faker->paragraph, + 'button_text' => $this->faker->word, + ], + ]; + } +} diff --git a/database/migrations/create_global_block_configs_table.php.stub b/database/migrations/create_global_block_configs_table.php.stub new file mode 100644 index 0000000..16d7722 --- /dev/null +++ b/database/migrations/create_global_block_configs_table.php.stub @@ -0,0 +1,26 @@ +id(); + $table->string('name'); + $table->string('class_name')->unique(); + $table->json('configuration')->nullable(); + $table->timestamps(); + + $table->index('class_name'); + }); + } + + public function down() + { + Schema::dropIfExists('global_block_configs'); + } +}; \ No newline at end of file diff --git a/src/Commands/CreatePageBuilderPluginBlockCommand.php b/src/Commands/CreatePageBuilderPluginBlockCommand.php index 976b27d..e73f82b 100644 --- a/src/Commands/CreatePageBuilderPluginBlockCommand.php +++ b/src/Commands/CreatePageBuilderPluginBlockCommand.php @@ -14,7 +14,7 @@ class CreatePageBuilderPluginBlockCommand extends Command { use CreatesClassFile; - public $signature = 'page-builder-plugin:make-block {name?} {--T|type=} {--panel=}'; + public $signature = 'page-builder-plugin:make-block {name?} {--T|type=} {--panel=} {--global : Create a global block in Blocks/Globals directory}'; public $description = 'create a new block'; @@ -37,8 +37,16 @@ public function handle(): int $this->panel = $this->getPanelToCreateIn(); + $isGlobal = $this->option('global'); + $blocksNamespace = $this->getClassNameSpaces('Blocks'); + if ($isGlobal) { + $this->createGlobalsCategoryIfNotExists(); + $isFirstGlobalBlock = $this->isFirstGlobalBlock(); + $blocksNamespace .= '\\Globals'; + } + $blockClass = $blocksNamespace . '\\' . $block; try { @@ -60,40 +68,96 @@ public function handle(): int ); if ($blockType === 'iframe') { + $stubName = $isGlobal ? 'block.global' : 'block'; + $replacements = [ + '{{ class }}' => str($blockClass)->afterLast('\\')->replace('\\', ''), + '{{ namespace }}' => str($blockClass)->beforeLast('\\'), + ]; + + if ($isGlobal) { + $replacements['{{ globalsNamespace }}'] = $this->getClassNameSpaces('BlockCategories'); + } + $this->createFileFromStub( - 'block', + $stubName, $this->appClassToPath($blockClass), - [ - '{{ class }}' => str($blockClass)->afterLast('\\')->replace('\\', ''), - '{{ namespace }}' => str($blockClass)->beforeLast('\\'), - ] + $replacements ); } if ($blockType === 'view') { $viewName = str($block)->replace('\\', '.')->kebab()->replace('.-', '.'); + if ($isGlobal) { + $viewName = 'globals.' . $viewName; + } + + $stubName = $isGlobal ? 'block.global.view' : 'block.view'; + $replacements = [ + '{{ class }}' => str($block)->afterLast('\\')->replace('\\', ''), + '{{ namespace }}' => str($blockClass)->beforeLast('\\'), + '{{ viewName }}' => $viewName, + '{{ panelId }}' => $this->panel->getId(), + ]; + + if ($isGlobal) { + $replacements['{{ globalsNamespace }}'] = $this->getClassNameSpaces('BlockCategories'); + } + $this->createFileFromStub( - 'block.view', + $stubName, $this->appClassToPath($blockClass), - [ - '{{ class }}' => str($block)->afterLast('\\')->replace('\\', ''), - '{{ namespace }}' => str($blockClass)->beforeLast('\\'), - '{{ viewName }}' => $viewName, - '{{ panelId }}' => $this->panel->getId(), - ] + $replacements ); + $viewPath = str($viewName) + ->replace('.', '/') + ->prepend("views/{$this->panel->getId()}/blocks/") + ->append('.blade.php'); + $this->createFileFromStub( 'block.blade', - resource_path( - $viewName - ->replace('.', '/') - ->prepend("views/{$this->panel->getId()}/blocks/") - ->append('.blade.php') - ), + resource_path($viewPath), ); } + if ($isGlobal && isset($isFirstGlobalBlock) && $isFirstGlobalBlock) { + $this->info('To manage global blocks in Filament, add the GlobalBlocksPlugin to your panel:'); + } + return self::SUCCESS; } + + protected function createGlobalsCategoryIfNotExists(): void + { + $categoryNamespace = $this->getClassNameSpaces('BlockCategories'); + $globalsClass = $categoryNamespace . '\\Globals'; + + if (class_exists($globalsClass)) { + return; + } + + $this->createFileFromStub( + 'category-block', + $this->appClassToPath($globalsClass), + [ + '{{ class }}' => 'Globals', + '{{ namespace }}' => $categoryNamespace, + ] + ); + + $this->info("Created Globals category at: {$globalsClass}"); + } + + protected function isFirstGlobalBlock(): bool + { + $globalBlocksPath = app_path("Filament/{$this->panel->getId()}/Blocks/Globals"); + + if (! is_dir($globalBlocksPath)) { + return true; + } + + $existingBlocks = glob($globalBlocksPath . '/*.php'); + + return empty($existingBlocks); + } } diff --git a/src/Components/Forms/Actions/SelectBlockAction.php b/src/Components/Forms/Actions/SelectBlockAction.php index c055132..39cdd58 100644 --- a/src/Components/Forms/Actions/SelectBlockAction.php +++ b/src/Components/Forms/Actions/SelectBlockAction.php @@ -5,6 +5,7 @@ use Closure; use Filament\Forms\Components\Actions\Action; use Filament\Forms\Components\Select; +use Filament\Notifications\Notification; use Filament\Support\Enums\MaxWidth; use Illuminate\View\ComponentAttributeBag; use Redberry\PageBuilderPlugin\Components\Forms\PageBuilder; @@ -86,6 +87,36 @@ protected function setUp(): void $this->stickyModalFooter(); $this->action(function ($data, $livewire, PageBuilder $component) { + $blockType = $data['block_type']; + + $isGlobalBlock = class_exists($blockType) && method_exists($blockType, 'isGlobalBlock') && $blockType::isGlobalBlock(); + + if ($isGlobalBlock) { + $state = $component->getState() ?? []; + + $block = app($component->getModel())->{$component->relationship}()->make([ + 'block_type' => $blockType, + 'data' => [], + 'order' => count($state) + 1, + ]); + + $block->id = $block->newUniqueId(); + + $component->state([ + ...$state, + $block->toArray(), + ]); + + $component->callAfterStateUpdated(); + + Notification::make() + ->title('Block added successfully') + ->success() + ->send(); + + return; + } + $livewire->mountFormComponentAction( $component->getStatePath(), $component->getCreateActionName(), diff --git a/src/GlobalBlocksPlugin.php b/src/GlobalBlocksPlugin.php new file mode 100644 index 0000000..05943ca --- /dev/null +++ b/src/GlobalBlocksPlugin.php @@ -0,0 +1,58 @@ +enableGlobalBlocksResource = $enable; + + return $this; + } + + public function resource(string $resourceClass): static + { + $this->globalResourceClass = $resourceClass; + + return $this; + } + + public function getId(): string + { + return 'page-builder-global-blocks'; + } + + public function register(Panel $panel): void + { + if (! $this->enableGlobalBlocksResource) { + return; + } + + if (! class_exists($this->globalResourceClass)) { + return; + } + + $panel->resources([ + $this->globalResourceClass, + ]); + } + + public function boot(Panel $panel): void + { + // + } +} diff --git a/src/Models/GlobalBlockConfig.php b/src/Models/GlobalBlockConfig.php new file mode 100644 index 0000000..748f39a --- /dev/null +++ b/src/Models/GlobalBlockConfig.php @@ -0,0 +1,159 @@ + 'array', + ]; + + /** + * Get the configuration value for a specific field + */ + public function getConfigValue(string $field, $default = null) + { + return data_get($this->configuration, $field, $default); + } + + /** + * Set a configuration value for a specific field + */ + public function setConfigValue(string $field, $value): void + { + $config = $this->configuration ?? []; + $config[$field] = $value; + $this->configuration = $config; + } + + /** + * Get configuration for a specific global block class + */ + public static function getForClass(string $className): ?self + { + if (! static::tableExists()) { + return null; + } + + return static::where('class_name', $className)->first(); + } + + /** + * Refresh global blocks by scanning for new global block classes + */ + public static function refreshGlobalBlocks(): void + { + if (! static::tableExists()) { + return; + } + + $globalBlocks = static::discoverGlobalBlocks(); + + foreach ($globalBlocks as $blockClass) { + static::firstOrCreate([ + 'class_name' => $blockClass, + ], [ + 'name' => static::getBlockDisplayName($blockClass), + 'configuration' => static::getDefaultConfiguration($blockClass), + ]); + } + } + + /** + * Discover all global block classes + */ + protected static function discoverGlobalBlocks(): array + { + $globalBlocks = []; + $appPath = app_path(); + + $globalsDirectories = File::glob($appPath . '/Filament/*/Blocks/Globals'); + + foreach ($globalsDirectories as $directory) { + $files = File::glob($directory . '/*.php'); + + foreach ($files as $file) { + $relativePath = str_replace($appPath . '/', '', $file); + $relativePath = str_replace('.php', '', $relativePath); + $className = 'App\\' . str_replace('/', '\\', $relativePath); + + if (class_exists($className)) { + $reflection = new ReflectionClass($className); + if (! $reflection->isAbstract() && $reflection->isSubclassOf('Redberry\\PageBuilderPlugin\\Abstracts\\BaseBlock')) { + $globalBlocks[] = $className; + } + } + } + } + + return $globalBlocks; + } + + /** + * Get display name for a block class + */ + protected static function getBlockDisplayName(string $className): string + { + $shortName = class_basename($className); + + return str($shortName)->headline()->toString(); + } + + /** + * Get default configuration for a block class by analyzing its schema + */ + protected static function getDefaultConfiguration(string $className): array + { + if (! class_exists($className)) { + return []; + } + + try { + if (method_exists($className, 'getBaseBlockSchema')) { + $schema = $className::getBaseBlockSchema(); + } else { + $schema = $className::getBlockSchema(); + } + + $config = []; + + foreach ($schema as $field) { + if (method_exists($field, 'getName')) { + $fieldName = $field->getName(); + $config[$fieldName] = null; + } + } + + return $config; + } catch (\Exception $e) { + return []; + } + } + + /** + * Check if the table exists in the database + */ + public static function tableExists(): bool + { + try { + return \Schema::hasTable('global_block_configs'); + } catch (\Exception $e) { + return false; + } + } +} diff --git a/src/PageBuilderPluginServiceProvider.php b/src/PageBuilderPluginServiceProvider.php index 29dc76d..1861ea5 100644 --- a/src/PageBuilderPluginServiceProvider.php +++ b/src/PageBuilderPluginServiceProvider.php @@ -52,7 +52,10 @@ public function configurePackage(Package $package): void } } - public function packageRegistered(): void {} + public function packageRegistered(): void + { + $this->app->singleton(GlobalBlocksPlugin::class); + } public function packageBooted(): void { @@ -140,6 +143,7 @@ protected function getMigrations(): array { return [ 'create_page_builder_blocks_table', + 'create_global_block_configs_table', ]; } } diff --git a/src/Resources/GlobalBlockConfigResource.php b/src/Resources/GlobalBlockConfigResource.php new file mode 100644 index 0000000..716ac1b --- /dev/null +++ b/src/Resources/GlobalBlockConfigResource.php @@ -0,0 +1,111 @@ +schema([ + Forms\Components\TextInput::make('name') + ->label('Block Name') + ->required() + ->disabled(), + + Forms\Components\Section::make('Block Configuration') + ->schema(function (?GlobalBlockConfig $record) { + if (! $record || ! class_exists($record->class_name)) { + return []; + } + + try { + $blockClass = $record->class_name; + if (method_exists($blockClass, 'getBaseBlockSchema')) { + $schema = $blockClass::getBaseBlockSchema(); + } else { + $schema = $blockClass::getBlockSchema(); + } + + foreach ($schema as $field) { + if (method_exists($field, 'getName')) { + $fieldName = $field->getName(); + $configValue = $record->getConfigValue($fieldName); + if ($configValue !== null) { + $field->default($configValue); + } + } + } + + return $schema; + } catch (\Exception $e) { + return [ + Forms\Components\Placeholder::make('error') + ->label('Error') + ->content('Unable to load block schema: ' . $e->getMessage()), + ]; + } + }) + ->columnSpan(2), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('name') + ->label('Block Name') + ->searchable() + ->sortable(), + ]) + ->actions([ + Tables\Actions\EditAction::make() + ->label('Configure'), + ]) + ->bulkActions([ + // + ]); + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery()->orderBy('name'); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListGlobalBlocks::route('/'), + 'edit' => Pages\EditGlobalBlock::route('/{record}/edit'), + ]; + } +} diff --git a/src/Resources/GlobalBlockConfigResource/Pages/EditGlobalBlock.php b/src/Resources/GlobalBlockConfigResource/Pages/EditGlobalBlock.php new file mode 100644 index 0000000..1e25b38 --- /dev/null +++ b/src/Resources/GlobalBlockConfigResource/Pages/EditGlobalBlock.php @@ -0,0 +1,107 @@ +label('Preview Block') + ->icon('heroicon-o-eye') + ->color('gray') + ->url(function () { + return null; + }) + ->hidden(true), + ]; + } + + protected function mutateFormDataBeforeFill(array $data): array + { + $blockClass = $this->record->class_name; + + if (class_exists($blockClass) && $this->record->configuration) { + try { + if (method_exists($blockClass, 'getBaseBlockSchema')) { + $schema = $blockClass::getBaseBlockSchema(); + } else { + $schema = $blockClass::getBlockSchema(); + } + + foreach ($schema as $field) { + if (method_exists($field, 'getName')) { + $fieldName = $field->getName(); + $configValue = $this->record->getConfigValue($fieldName); + if ($configValue !== null) { + $data[$fieldName] = $configValue; + } + } + } + } catch (\Exception $e) { + } + } + + return $data; + } + + protected function mutateFormDataBeforeSave(array $data): array + { + $configuration = []; + $blockClass = $this->record->class_name; + + if (class_exists($blockClass)) { + try { + if (method_exists($blockClass, 'getBaseBlockSchema')) { + $schema = $blockClass::getBaseBlockSchema(); + } else { + $schema = $blockClass::getBlockSchema(); + } + + foreach ($schema as $field) { + if (method_exists($field, 'getName')) { + $fieldName = $field->getName(); + if (array_key_exists($fieldName, $data)) { + $configuration[$fieldName] = $data[$fieldName]; + } + } + } + } catch (\Exception $e) { + } + } + + $data['configuration'] = $configuration; + + if (class_exists($blockClass)) { + try { + if (method_exists($blockClass, 'getBaseBlockSchema')) { + $schema = $blockClass::getBaseBlockSchema(); + } else { + $schema = $blockClass::getBlockSchema(); + } + + foreach ($schema as $field) { + if (method_exists($field, 'getName')) { + $fieldName = $field->getName(); + unset($data[$fieldName]); + } + } + } catch (\Exception $e) { + } + } + + return $data; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('index'); + } +} diff --git a/src/Resources/GlobalBlockConfigResource/Pages/ListGlobalBlocks.php b/src/Resources/GlobalBlockConfigResource/Pages/ListGlobalBlocks.php new file mode 100644 index 0000000..ab098c0 --- /dev/null +++ b/src/Resources/GlobalBlockConfigResource/Pages/ListGlobalBlocks.php @@ -0,0 +1,35 @@ +label('Refresh Global Blocks') + ->icon('heroicon-o-arrow-path') + ->action('refreshGlobalBlocks') + ->color('gray'), + ]; + } + + public function refreshGlobalBlocks(): void + { + $this->getResource()::getModel()::refreshGlobalBlocks(); + + \Filament\Notifications\Notification::make() + ->title('Global blocks refreshed successfully') + ->success() + ->send(); + + $this->redirect($this->getResource()::getUrl('index')); + } +} diff --git a/src/Traits/IsGlobalBlock.php b/src/Traits/IsGlobalBlock.php new file mode 100644 index 0000000..19a6db9 --- /dev/null +++ b/src/Traits/IsGlobalBlock.php @@ -0,0 +1,93 @@ +configuration ?? []) : []; + } + + /** + * Create or update the global block configuration + */ + public static function updateGlobalConfig(array $configuration): void + { + $config = static::getGlobalConfig(); + + if ($config) { + $config->update(['configuration' => $configuration]); + } else { + GlobalBlockConfig::create([ + 'name' => static::getBlockDisplayName(), + 'class_name' => static::class, + 'configuration' => $configuration, + ]); + } + } + + /** + * Get display name for this block + */ + public static function getBlockDisplayName(): string + { + $shortName = class_basename(static::class); + + return str($shortName)->headline()->toString(); + } + + /** + * Override the format methods to use global configuration + */ + public static function formatForSingleView(array $data, ?object $record = null): array + { + $globalData = static::getGlobalBlockData(); + $mergedData = array_merge($data, $globalData); + + if (method_exists(parent::class, 'formatForSingleView')) { + return parent::formatForSingleView($mergedData, $record); + } + + return $mergedData; + } + + public static function formatForListingView(array $data, ?object $record = null): array + { + return static::formatForSingleView($data, $record); + } +} diff --git a/stubs/block.global.stub b/stubs/block.global.stub new file mode 100644 index 0000000..bf31ddd --- /dev/null +++ b/stubs/block.global.stub @@ -0,0 +1,31 @@ +id(); + $table->string('name'); + $table->string('class_name')->unique(); + $table->json('configuration')->nullable(); + $table->timestamps(); + + $table->index('class_name'); + }); + } + + public function down() + { + Schema::dropIfExists('global_block_configs'); + } +}; \ No newline at end of file diff --git a/tests/Fixtures/Blocks/GlobalViewBlock.php b/tests/Fixtures/Blocks/GlobalViewBlock.php new file mode 100644 index 0000000..0f9cbb3 --- /dev/null +++ b/tests/Fixtures/Blocks/GlobalViewBlock.php @@ -0,0 +1,36 @@ +label('Title') + ->required(), + + Textarea::make('content') + ->label('Content') + ->required(), + + TextInput::make('button_text') + ->label('Button Text'), + ]; + } +} diff --git a/tests/GlobalBlocksPluginTest.php b/tests/GlobalBlocksPluginTest.php new file mode 100644 index 0000000..46e0dfa --- /dev/null +++ b/tests/GlobalBlocksPluginTest.php @@ -0,0 +1,78 @@ +toBeInstanceOf(GlobalBlocksPlugin::class); +}); + +it('has correct plugin ID', function () { + $plugin = GlobalBlocksPlugin::make(); + + expect($plugin->getId())->toBe('page-builder-global-blocks'); +}); + +it('can configure enable global blocks', function () { + $plugin = GlobalBlocksPlugin::make() + ->enableGlobalBlocks(false); + + expect($plugin)->toBeInstanceOf(GlobalBlocksPlugin::class); +}); + +it('can configure custom resource', function () { + $customResourceClass = 'App\\Filament\\Resources\\CustomGlobalBlocksResource'; + + $plugin = GlobalBlocksPlugin::make() + ->resource($customResourceClass); + + expect($plugin)->toBeInstanceOf(GlobalBlocksPlugin::class); +}); + +it('registers resource when enabled', function () { + $panel = Panel::make(); + + $plugin = GlobalBlocksPlugin::make() + ->enableGlobalBlocks(true); + + $plugin->register($panel); + + expect($panel->getResources())->toContain(GlobalBlockConfigResource::class); +}); + +it('does not register resource when disabled', function () { + $panel = Panel::make(); + + $plugin = GlobalBlocksPlugin::make() + ->enableGlobalBlocks(false); + + $plugin->register($panel); + + expect($panel->getResources())->not->toContain(GlobalBlockConfigResource::class); +}); + +it('registers custom resource when specified', function () { + $customResourceClass = GlobalBlockConfigResource::class; // Using existing class for test + $panel = Panel::make(); + + $plugin = GlobalBlocksPlugin::make() + ->resource($customResourceClass); + + $plugin->register($panel); + + expect($panel->getResources())->toContain($customResourceClass); +}); + +it('handles non-existent resource class gracefully', function () { + $panel = Panel::make(); + + $plugin = GlobalBlocksPlugin::make() + ->resource('NonExistentResourceClass'); + + $plugin->register($panel); + + expect($panel->getResources())->toBeEmpty(); +}); diff --git a/tests/Models/GlobalBlockConfigTest.php b/tests/Models/GlobalBlockConfigTest.php new file mode 100644 index 0000000..7954bb3 --- /dev/null +++ b/tests/Models/GlobalBlockConfigTest.php @@ -0,0 +1,61 @@ +artisan('migrate', ['--database' => 'testing'])->run(); +}); + +afterEach(function () { + unlink(database_path('migrations/create_global_block_configs_table.php')); +}); + +it('can create a global block config', function () { + $config = GlobalBlockConfig::create([ + 'name' => 'Test Block', + 'class_name' => GlobalViewBlock::class, + 'configuration' => [ + 'title' => 'Test Title', + 'content' => 'Test Content', + ], + ]); + + expect($config)->toBeInstanceOf(GlobalBlockConfig::class) + ->and($config->name)->toBe('Test Block') + ->and($config->class_name)->toBe(GlobalViewBlock::class) + ->and($config->configuration)->toBe([ + 'title' => 'Test Title', + 'content' => 'Test Content', + ]); +}); + +it('can get config value', function () { + $config = GlobalBlockConfig::create([ + 'name' => 'Test Block', + 'class_name' => GlobalViewBlock::class, + 'configuration' => [ + 'title' => 'Test Title', + 'content' => 'Test Content', + ], + ]); + + expect($config->getConfigValue('title'))->toBe('Test Title') + ->and($config->getConfigValue('content'))->toBe('Test Content') + ->and($config->getConfigValue('non_existent'))->toBeNull(); +}); + +it('handles missing configuration gracefully', function () { + $config = GlobalBlockConfig::create([ + 'name' => 'Test Block', + 'class_name' => GlobalViewBlock::class, + 'configuration' => null, + ]); + + expect($config->getConfigValue('title'))->toBeNull(); +});