Files
filament/tests/src/Panels/Configuration/NavigationTest.php
T
ahmed-rashad-alnaggarandDan Harrin b986eee58f feat: Add strict hierarchical breadcrumbs option (#20153)
* Add strict hierarchical option to breadcrumbs method

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>

* Update Page.php

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>

* Update Page.php

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>

* Update Page.php

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>

* Add strict hierarchical breadcrumbs documentation

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>

* Implement getClusterHierarchicalBreadcrumbs method

Add method to retrieve hierarchical breadcrumbs for clusters.

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>

* Update Page.php

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>

* Update Page.php

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>

* Add strict hierarchical breadcrumb coverage

* Improve navigation hierarchy breadcrumbs

* Refine custom page breadcrumb defaults

* Support clustered resource record breadcrumbs

---------

Signed-off-by: ahmed-rashad-alnaggar <131385452+ahmed-rashad-alnaggar@users.noreply.github.com>
Co-authored-by: Dan Harrin <git@danharrin.com>
2026-09-26 16:07:21 +01:00

203 lines
8.4 KiB
PHP

<?php
use Filament\Facades\Filament;
use Filament\Navigation\NavigationItem;
use Filament\Tests\Fixtures\Pages\ConfigurableSettings;
use Filament\Tests\Fixtures\Resources\Posts\ConfigurablePostResource;
use Filament\Tests\Fixtures\Resources\Posts\Pages\ListConfigurablePosts;
use Filament\Tests\Panels\Configuration\TestCase;
uses(TestCase::class);
it('includes the navigation hierarchy in breadcrumbs for the active page and resource configurations', function (): void {
Filament::getCurrentOrDefaultPanel()->breadcrumbs(hasNavigationHierarchy: true);
Filament::forPageConfiguration(ConfigurableSettings::class, 'general');
expect(app(ConfigurableSettings::class)->getBreadcrumbs())->toBe([
'Settings',
'Configurable Settings',
]);
Filament::setCurrentPageConfigurationKey(null);
Filament::forResourceConfiguration(ConfigurablePostResource::class, 'archived');
expect(app(ListConfigurablePosts::class)->getResourceBreadcrumbs())->toBe([
'Archive',
ConfigurablePostResource::getUrl() => 'Posts',
]);
});
describe('resource configuration navigation', function (): void {
it('registers separate navigation items for each resource configuration', function (): void {
$navigation = Filament::getNavigation();
// Flatten navigation items from all groups
$items = collect($navigation)
->flatMap(fn ($group) => $group->getItems())
->filter(fn (NavigationItem $item) => str_contains($item->getUrl(), 'posts'))
->values();
// Should have 3 items: default, featured, and archived
expect($items)->toHaveCount(3);
$urls = $items->map(fn (NavigationItem $item) => $item->getUrl())->all();
expect($urls)->toContain(ConfigurablePostResource::getUrl());
expect($urls)->toContain(ConfigurablePostResource::getUrl(configuration: 'featured'));
expect($urls)->toContain(ConfigurablePostResource::getUrl(configuration: 'archived'));
});
it('registers separate navigation items for each resource configuration when one is active', function (): void {
$defaultUrl = ConfigurablePostResource::getUrl();
ConfigurablePostResource::withConfiguration('featured', static function () use ($defaultUrl): void {
$items = collect(Filament::getNavigation())
->flatMap(static fn ($group) => $group->getItems())
->filter(static fn (NavigationItem $item) => str_contains($item->getUrl(), 'posts'))
->values();
expect($items)->toHaveCount(3);
$urls = $items->map(static fn (NavigationItem $item) => $item->getUrl())->all();
expect($urls)->toContain($defaultUrl);
expect($urls)->toContain(ConfigurablePostResource::getUrl(configuration: 'featured'));
expect($urls)->toContain(ConfigurablePostResource::getUrl(configuration: 'archived'));
expect(ConfigurablePostResource::getConfiguration()?->getKey())->toBe('featured');
});
});
it('preserves the active resource configuration key when mounting navigation', function (): void {
Filament::forResourceConfiguration(ConfigurablePostResource::class, 'featured');
Filament::getNavigation();
expect(Filament::getCurrentResourceConfigurationKey())->toBe('featured');
});
it('shows correct navigation labels for resource configurations', function (): void {
$navigation = Filament::getNavigation();
// Flatten navigation items from all groups
$items = collect($navigation)
->flatMap(fn ($group) => $group->getItems())
->filter(fn (NavigationItem $item) => str_contains($item->getUrl(), 'posts'))
->values();
$labels = $items->map(fn (NavigationItem $item) => $item->getLabel())->all();
expect($labels)->toContain('Posts'); // Default
expect($labels)->toContain('Featured Posts');
expect($labels)->toContain('Archived Posts');
});
it('places resource configuration navigation items in correct groups', function (): void {
$navigation = Filament::getNavigation();
// Find groups that contain post-related items
$groupsWithPosts = collect($navigation)
->filter(function ($group) {
return collect($group->getItems())
->filter(fn (NavigationItem $item) => str_contains($item->getUrl(), 'posts'))
->isNotEmpty();
})
->map(fn ($group) => $group->getLabel())
->values()
->all();
expect($groupsWithPosts)->toContain('Blog'); // Default
expect($groupsWithPosts)->toContain('Featured Content');
expect($groupsWithPosts)->toContain('Archive');
});
});
describe('page configuration navigation', function (): void {
it('registers separate navigation items for each page configuration', function (): void {
$navigation = Filament::getNavigation();
// Flatten navigation items from all groups
$items = collect($navigation)
->flatMap(fn ($group) => $group->getItems())
->filter(fn (NavigationItem $item) => str_contains($item->getUrl(), 'settings'))
->values();
// Should have 3 items: default, general, and advanced
expect($items)->toHaveCount(3);
$urls = $items->map(fn (NavigationItem $item) => $item->getUrl())->all();
expect($urls)->toContain(ConfigurableSettings::getUrl());
expect($urls)->toContain(ConfigurableSettings::withConfiguration('general', fn () => ConfigurableSettings::getUrl()));
expect($urls)->toContain(ConfigurableSettings::withConfiguration('advanced', fn () => ConfigurableSettings::getUrl()));
});
it('registers separate navigation items for each page configuration when one is active', function (): void {
$defaultUrl = ConfigurableSettings::getUrl();
ConfigurableSettings::withConfiguration('general', static function () use ($defaultUrl): void {
$items = collect(Filament::getNavigation())
->flatMap(static fn ($group) => $group->getItems())
->filter(static fn (NavigationItem $item) => str_contains($item->getUrl(), 'settings'))
->values();
expect($items)->toHaveCount(3);
$urls = $items->map(static fn (NavigationItem $item) => $item->getUrl())->all();
expect($urls)->toContain($defaultUrl);
expect($urls)->toContain(ConfigurableSettings::getUrl(configuration: 'general'));
expect($urls)->toContain(ConfigurableSettings::getUrl(configuration: 'advanced'));
expect(ConfigurableSettings::getConfiguration()?->getKey())->toBe('general');
});
});
it('preserves the active page configuration key when mounting navigation', function (): void {
Filament::forPageConfiguration(ConfigurableSettings::class, 'general');
Filament::getNavigation();
expect(Filament::getCurrentPageConfigurationKey())->toBe('general');
});
it('shows correct navigation labels for page configurations', function (): void {
$navigation = Filament::getNavigation();
// Flatten navigation items from all groups
$items = collect($navigation)
->flatMap(fn ($group) => $group->getItems())
->filter(fn (NavigationItem $item) => str_contains($item->getUrl(), 'settings'))
->values();
$labels = $items->map(fn (NavigationItem $item) => $item->getLabel())->all();
expect($labels)->toContain('Configurable Settings'); // Default
expect($labels)->toContain('General Settings');
expect($labels)->toContain('Advanced Settings');
});
it('places page configuration navigation items in correct groups', function (): void {
$navigation = Filament::getNavigation();
// Find items in Settings group
$settingsGroup = collect($navigation)
->first(fn ($group) => $group->getLabel() === 'Settings');
expect($settingsGroup)->not->toBeNull();
$items = collect($settingsGroup->getItems())
->filter(fn (NavigationItem $item) => str_contains($item->getUrl(), 'settings'))
->values();
// Both general and advanced should be in Settings group
expect($items)->toHaveCount(2);
$labels = $items->map(fn (NavigationItem $item) => $item->getLabel())->all();
expect($labels)->toContain('General Settings');
expect($labels)->toContain('Advanced Settings');
});
});