Frontend: App shell, navigation, routing, and design tokens #32
@@ -32,6 +32,7 @@
|
||||
? 'bg-[var(--green-tint)] text-[var(--green-dark)] font-medium'
|
||||
: ''}"
|
||||
>
|
||||
<span class="w-[20px] text-[16px] text-center">{item.icon}</span>
|
||||
{item.label}
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
@@ -23,11 +23,11 @@ describe('MobileTabBar active state per route', () => {
|
||||
pageStore.set({ url: new URL(`http://localhost${route}`) });
|
||||
const { unmount } = render(MobileTabBar);
|
||||
|
||||
const activeLink = screen.getByRole('link', { name: expectedActiveLabel });
|
||||
const activeLink = screen.getByRole('link', { name: new RegExp(expectedActiveLabel) });
|
||||
expect(activeLink).toHaveAttribute('aria-current', 'page');
|
||||
|
||||
const allLinks = screen.getAllByRole('link');
|
||||
const inactiveLinks = allLinks.filter((link) => link.textContent?.trim() !== expectedActiveLabel);
|
||||
const inactiveLinks = allLinks.filter((link) => !link.textContent?.includes(expectedActiveLabel));
|
||||
for (const link of inactiveLinks) {
|
||||
expect(link).not.toHaveAttribute('aria-current');
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
? 'bg-[var(--green-tint)] text-[var(--green-dark)] font-medium'
|
||||
: ''}"
|
||||
>
|
||||
<span class="text-[16px]">{item.icon}</span>
|
||||
{item.label}
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
@@ -39,6 +39,14 @@ describe('MobileTabBar', () => {
|
||||
expect(plannerLink).toHaveAttribute('aria-current', 'page');
|
||||
});
|
||||
|
||||
it('renders icons for each nav item', () => {
|
||||
render(MobileTabBar);
|
||||
expect(screen.getByText('📅')).toBeInTheDocument();
|
||||
expect(screen.getByText('📖')).toBeInTheDocument();
|
||||
expect(screen.getByText('🛒')).toBeInTheDocument();
|
||||
expect(screen.getByText('⚙️')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('non-active items do not have aria-current', () => {
|
||||
render(MobileTabBar);
|
||||
const recipesLink = screen.getByRole('link', { name: /rezepte/i });
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
? 'bg-[var(--green-tint)] text-[var(--green-dark)] font-medium'
|
||||
: ''}"
|
||||
>
|
||||
<span>{item.icon}</span>
|
||||
{item.label}
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
@@ -10,10 +10,10 @@ export interface NavSection {
|
||||
}
|
||||
|
||||
export const mobileNavItems: NavItem[] = [
|
||||
{ href: '/planner', label: 'Planer', icon: 'calendar' },
|
||||
{ href: '/recipes', label: 'Rezepte', icon: 'book' },
|
||||
{ href: '/shopping', label: 'Einkauf', icon: 'shopping-cart' },
|
||||
{ href: '/settings', label: 'Einstellungen', icon: 'settings' }
|
||||
{ href: '/planner', label: 'Planer', icon: '📅' },
|
||||
{ href: '/recipes', label: 'Rezepte', icon: '📖' },
|
||||
{ href: '/shopping', label: 'Einkauf', icon: '🛒' },
|
||||
{ href: '/settings', label: 'Einstellungen', icon: '⚙️' }
|
||||
];
|
||||
|
||||
export function isActiveRoute(href: string, pathname: string): boolean {
|
||||
@@ -24,16 +24,16 @@ export const desktopNavSections: NavSection[] = [
|
||||
{
|
||||
title: 'Plan',
|
||||
items: [
|
||||
{ href: '/planner', label: 'Planer', icon: 'calendar' },
|
||||
{ href: '/recipes', label: 'Rezepte', icon: 'book' },
|
||||
{ href: '/shopping', label: 'Einkauf', icon: 'shopping-cart' }
|
||||
{ href: '/planner', label: 'Planer', icon: '📅' },
|
||||
{ href: '/recipes', label: 'Rezepte', icon: '📖' },
|
||||
{ href: '/shopping', label: 'Einkauf', icon: '🛒' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Haushalt',
|
||||
items: [
|
||||
{ href: '/members', label: 'Mitglieder', icon: 'users' },
|
||||
{ href: '/settings', label: 'Einstellungen', icon: 'settings' }
|
||||
{ href: '/members', label: 'Mitglieder', icon: '👥' },
|
||||
{ href: '/settings', label: 'Einstellungen', icon: '⚙️' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user