mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-26 01:10:30 -04:00
fix(ui): collapse non-active sub-sections + accordion on inline service nav
Follow-up to the settings-sidebar accordion. Two gaps: - Application sidebar rendered every item's in-page sub-sections, so Advanced's Build/Container/Deployment/… showed while you were on General. Gate the nav-children on the active item so only the current page's sub-sections expand (database/service/server already did this). - The service *main* configuration page renders its nav inline (the component is only used by service sub-pages), so it missed the accordion. Wire it up there too. Extends the regression test to cover both.
This commit is contained in:
@@ -288,23 +288,17 @@
|
||||
</span>
|
||||
@endif
|
||||
</a>
|
||||
@if (filled($sections))
|
||||
{{-- Sub-sections belong to the current page only; collapse them for
|
||||
every other item so the sidebar stays short. --}}
|
||||
@if ($menuItem['active'] && filled($sections))
|
||||
<div class="nav-children hidden flex-col gap-0.5 py-1 xl:flex"
|
||||
x-data="{ activeSection: '' }">
|
||||
@foreach ($sections as $section)
|
||||
@if ($menuItem['active'])
|
||||
<button type="button" class="menu-subitem"
|
||||
:class="activeSection === '{{ $section['id'] }}' && 'menu-subitem-active'"
|
||||
x-on:click="activeSection = '{{ $section['id'] }}'; history.replaceState(null, '', '#{{ $section['id'] }}'); window.scrollToSettingsSection?.('{{ $section['id'] }}')">
|
||||
<span class="menu-item-label text-left">{{ $section['label'] }}</span>
|
||||
</button>
|
||||
@else
|
||||
<a class="menu-subitem"
|
||||
href="{{ route($menuItem['route'], $applicationRouteParameters) }}#{{ $section['id'] }}"
|
||||
{{ wireNavigate() }}>
|
||||
<span class="menu-item-label text-left">{{ $section['label'] }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<button type="button" class="menu-subitem"
|
||||
:class="activeSection === '{{ $section['id'] }}' && 'menu-subitem-active'"
|
||||
x-on:click="activeSection = '{{ $section['id'] }}'; history.replaceState(null, '', '#{{ $section['id'] }}'); window.scrollToSettingsSection?.('{{ $section['id'] }}')">
|
||||
<span class="menu-item-label text-left">{{ $section['label'] }}</span>
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
->values())
|
||||
->filter(fn ($items) => $items->isNotEmpty());
|
||||
|
||||
// Group that holds the current page — the only one expanded by default.
|
||||
$activeGroup = (string) $groupedItems->search(fn ($items) => $items->contains(fn ($item) => $item['active'] ?? false));
|
||||
|
||||
$storageSections = $applications
|
||||
->concat($databases)
|
||||
->map(fn ($resource): array => [
|
||||
@@ -59,13 +62,23 @@
|
||||
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-8">
|
||||
<aside class="application-settings-navigation min-w-0 xl:self-start">
|
||||
<nav aria-label="Service settings"
|
||||
x-data="settingsSidebarAccordion({ activeGroup: @js($activeGroup), storageKey: 'coolify.settings-sidebar.service' })"
|
||||
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
|
||||
@foreach ($groupedItems as $groupLabel => $groupItems)
|
||||
@unless ($loop->first)
|
||||
<div class="my-2 hidden border-t border-neutral-200 xl:block dark:border-white/[0.06]"
|
||||
aria-hidden="true"></div>
|
||||
@endunless
|
||||
<div class="nav-section hidden xl:block">{{ $groupLabel }}</div>
|
||||
<button type="button" class="nav-section-toggle hidden xl:flex" @click="toggle(@js($groupLabel))"
|
||||
:aria-expanded="isOpen(@js($groupLabel))">
|
||||
<span>{{ $groupLabel }}</span>
|
||||
<svg class="size-3 shrink-0 opacity-60 transition-transform"
|
||||
:class="!isOpen(@js($groupLabel)) && '-rotate-90'" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m6 9 6 6 6-6" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="contents" :class="isOpen(@js($groupLabel)) ? 'xl:block' : 'xl:hidden'">
|
||||
@foreach ($groupItems as $menuItem)
|
||||
<a @class([
|
||||
'menu-item',
|
||||
@@ -93,6 +106,7 @@
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
@@ -8,6 +8,7 @@ $groupedSidebars = [
|
||||
'application' => 'resources/views/components/application/configuration-sidebar.blade.php',
|
||||
'database' => 'resources/views/components/database/configuration-sidebar.blade.php',
|
||||
'service' => 'resources/views/components/service/configuration-sidebar.blade.php',
|
||||
'service-page' => 'resources/views/livewire/project/service/configuration.blade.php',
|
||||
'server' => 'resources/views/components/server/sidebar.blade.php',
|
||||
];
|
||||
|
||||
@@ -18,10 +19,20 @@ it('wires the collapsible accordion into every grouped settings sidebar', functi
|
||||
->toContain('settingsSidebarAccordion(') // shared Alpine data provider
|
||||
->toContain('$activeGroup') // only the active group opens by default
|
||||
->toContain('nav-section-toggle') // group header is a toggle button
|
||||
->toContain("toggle(") // header collapses/expands the group
|
||||
->toContain('toggle(') // header collapses/expands the group
|
||||
->toContain("? 'xl:block' : 'xl:hidden'"); // desktop-only collapse wrapper
|
||||
})->with($groupedSidebars);
|
||||
|
||||
it('only expands in-page sub-sections for the active page', function () {
|
||||
// Regression: the application sidebar used to render every item's sub-sections
|
||||
// (Advanced's Build/Container/… showed while you were on General).
|
||||
$contents = file_get_contents(base_path('resources/views/components/application/configuration-sidebar.blade.php'));
|
||||
|
||||
expect($contents)
|
||||
->toContain("\$menuItem['active'] && filled(\$sections)")
|
||||
->not->toContain('@if (filled($sections))');
|
||||
});
|
||||
|
||||
it('registers the accordion Alpine provider', function () {
|
||||
expect(file_get_contents(base_path('resources/js/app.js')))
|
||||
->toContain('initializeSettingsSidebarAccordionComponent');
|
||||
|
||||
Reference in New Issue
Block a user