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) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 11:30:06 +02:00
parent 4ff9a239fd
commit 3cb43d6cc0
2 changed files with 36 additions and 2 deletions
@@ -40,9 +40,13 @@ vi.mock('next-intl', () => ({
}, },
})); }));
let mockCollapsed = false;
function mockSidebarCollapsed(value: boolean) {
mockCollapsed = value;
}
vi.mock('@/lib/stores/sidebar-store', () => ({ vi.mock('@/lib/stores/sidebar-store', () => ({
useSidebarStore: () => ({ useSidebarStore: () => ({
isCollapsed: false, isCollapsed: mockCollapsed,
isMobileOpen: false, isMobileOpen: false,
toggle: vi.fn(), toggle: vi.fn(),
setMobileOpen: vi.fn(), setMobileOpen: vi.fn(),
@@ -108,6 +112,7 @@ afterEach(() => {
mockPathname = '/'; mockPathname = '/';
mockRefreshKey = 0; mockRefreshKey = 0;
mockCustomModules = []; mockCustomModules = [];
mockCollapsed = false;
}); });
function customModule(overrides: Partial<(typeof mockCustomModules)[number]> = {}) { function customModule(overrides: Partial<(typeof mockCustomModules)[number]> = {}) {
@@ -325,6 +330,24 @@ describe('Sidebar', () => {
expect(screen.queryByText('custom-modules')).toBeNull(); 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(<Sidebar />);
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 () => { it('eingebaute Module stehen innerhalb einer Kategorie vor eigenen', async () => {
mockCustomModules = [customModule({ category: 'Domain-Tools', name: 'Aaa Eigenes' })]; mockCustomModules = [customModule({ category: 'Domain-Tools', name: 'Aaa Eigenes' })];
const Sidebar = await importSidebar(); const Sidebar = await importSidebar();
+12 -1
View File
@@ -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 filteredCategories = useMemo(() => {
const categories = new Map<string, SidebarEntry[]>(); const categories = new Map<string, SidebarEntry[]>();
for (const entry of entries) { for (const entry of entries) {
@@ -253,7 +264,7 @@ export function Sidebar() {
{/* Eingeklappt: nur die Modul-Kacheln untereinander (Name als Tooltip). */} {/* Eingeklappt: nur die Modul-Kacheln untereinander (Name als Tooltip). */}
{isCollapsed && entries.length > 0 && ( {isCollapsed && entries.length > 0 && (
<ul className="mt-3 flex flex-col gap-px border-t border-sidebar-border pt-3"> <ul className="mt-3 flex flex-col gap-px border-t border-sidebar-border pt-3">
{entries.map((entry) => { {collapsedEntries.map((entry) => {
const active = isEntryActive(entry); const active = isEntryActive(entry);
return ( return (
<li key={entry.key}> <li key={entry.key}>