From 3cb43d6cc03f9fdd656c987f98747241a507326a Mon Sep 17 00:00:00 2001 From: Schalli Date: Tue, 29 Sep 2026 11:30:06 +0200 Subject: [PATCH] fix(web): eingeklappt stehen Eintraege aus "Eigene Module" zuletzt Die eingeklappte Seitenleiste zeigte die Kacheln in Ladefolge; ein Eintrag aus "Eigene Module" konnte so vor einem eigenen Eintrag anderer Kategorien stehen. Jetzt gleiche Gruppenfolge wie ausgeklappt. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/components/layout/sidebar.test.tsx | 25 ++++++++++++++++++- apps/web/src/components/layout/sidebar.tsx | 13 +++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/layout/sidebar.test.tsx b/apps/web/src/components/layout/sidebar.test.tsx index fe961f9..9de6981 100644 --- a/apps/web/src/components/layout/sidebar.test.tsx +++ b/apps/web/src/components/layout/sidebar.test.tsx @@ -40,9 +40,13 @@ vi.mock('next-intl', () => ({ }, })); +let mockCollapsed = false; +function mockSidebarCollapsed(value: boolean) { + mockCollapsed = value; +} vi.mock('@/lib/stores/sidebar-store', () => ({ useSidebarStore: () => ({ - isCollapsed: false, + isCollapsed: mockCollapsed, isMobileOpen: false, toggle: vi.fn(), setMobileOpen: vi.fn(), @@ -108,6 +112,7 @@ afterEach(() => { mockPathname = '/'; mockRefreshKey = 0; mockCustomModules = []; + mockCollapsed = false; }); function customModule(overrides: Partial<(typeof mockCustomModules)[number]> = {}) { @@ -325,6 +330,24 @@ describe('Sidebar', () => { expect(screen.queryByText('custom-modules')).toBeNull(); }); + it('eingeklappt stehen Eintraege aus „Eigene Module“ zuletzt', async () => { + mockCustomModules = [ + customModule({ id: 'cm-1', category: 'custom-modules', name: 'Mein Wiki' }), + customModule({ id: 'cm-2', category: 'fleet', name: 'Tankkarte' }), + ]; + mockSidebarCollapsed(true); + const Sidebar = await importSidebar(); + render(); + + const hrefs = () => + Array.from(document.querySelectorAll('aside ul.border-t a')).map((a) => a.getAttribute('href')); + await waitFor(() => { + expect(hrefs()).toContain('/modules/custom/cm-1'); + }); + expect(hrefs().at(-1)).toBe('/modules/custom/cm-1'); + expect(hrefs().indexOf('/modules/custom/cm-2')).toBeLessThan(hrefs().indexOf('/modules/custom/cm-1')); + }); + it('eingebaute Module stehen innerhalb einer Kategorie vor eigenen', async () => { mockCustomModules = [customModule({ category: 'Domain-Tools', name: 'Aaa Eigenes' })]; const Sidebar = await importSidebar(); diff --git a/apps/web/src/components/layout/sidebar.tsx b/apps/web/src/components/layout/sidebar.tsx index cadb256..7123811 100644 --- a/apps/web/src/components/layout/sidebar.tsx +++ b/apps/web/src/components/layout/sidebar.tsx @@ -186,6 +186,17 @@ export function Sidebar() { }); }; + // Eingeklappt in derselben Gruppenfolge wie ausgeklappt — „Eigene Module“ + // also auch hier immer zuletzt. + const collapsedEntries = useMemo( + () => + [...entries].sort( + (a, b) => + Number(a.category === CUSTOM_MODULE_CATEGORY) - Number(b.category === CUSTOM_MODULE_CATEGORY), + ), + [entries], + ); + const filteredCategories = useMemo(() => { const categories = new Map(); for (const entry of entries) { @@ -253,7 +264,7 @@ export function Sidebar() { {/* Eingeklappt: nur die Modul-Kacheln untereinander (Name als Tooltip). */} {isCollapsed && entries.length > 0 && (
    - {entries.map((entry) => { + {collapsedEntries.map((entry) => { const active = isEntryActive(entry); return (