fix(header): close navigation dropdown immediately on pathname change
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Successful in 1m9s
Build & Deploy / 🏗️ Build (push) Successful in 2m39s
Build & Deploy / 🚀 Deploy (push) Successful in 26s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Successful in 1m9s
Build & Deploy / 🏗️ Build (push) Successful in 2m39s
Build & Deploy / 🚀 Deploy (push) Successful in 26s
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
86
components/layout/Header.test.tsx
Normal file
86
components/layout/Header.test.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import * as React from 'react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, act, waitFor } from '@testing-library/react';
|
||||
import { Header, NavLink } from './Header';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
// Mock next/navigation
|
||||
vi.mock('next/navigation', () => ({
|
||||
usePathname: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock TransitionLink to render standard anchor tag and forward all props
|
||||
vi.mock('@/components/ui/TransitionLink', () => ({
|
||||
TransitionLink: ({ children, href, onClick, ...props }: any) => (
|
||||
<a href={href} onClick={onClick} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock next/image
|
||||
vi.mock('next/image', () => ({
|
||||
default: ({ src, alt, ...props }: any) => <img src={src} alt={alt} {...props} />,
|
||||
}));
|
||||
|
||||
// Mock LanguageSwitcher
|
||||
vi.mock('./LanguageSwitcher', () => ({
|
||||
LanguageSwitcher: () => <div data-testid="language-switcher" />,
|
||||
}));
|
||||
|
||||
describe('Header Dropdown Navigation TDD', () => {
|
||||
const mockNavLinks: NavLink[] = [
|
||||
{
|
||||
label: 'Home',
|
||||
url: '/',
|
||||
},
|
||||
{
|
||||
label: 'Leistungen',
|
||||
url: '/leistungen',
|
||||
children: [
|
||||
{ label: 'Kabelbau', url: '/leistungen/kabelbau' },
|
||||
{ label: 'Bohrtechnik', url: '/leistungen/bohrtechnik' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('closes the dropdown menu immediately after a page navigation occurs', async () => {
|
||||
// 1. Set initial pathname
|
||||
let currentPath = '/de';
|
||||
vi.mocked(usePathname).mockImplementation(() => currentPath);
|
||||
|
||||
// 2. Render Header
|
||||
const { rerender } = render(<Header navLinks={mockNavLinks} />);
|
||||
|
||||
// 3. Hover over "Leistungen" menu item to open dropdown
|
||||
const leistungenNavItem = screen.getByText('Leistungen');
|
||||
const navItemContainer = leistungenNavItem.closest('.group');
|
||||
expect(navItemContainer).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
fireEvent.mouseEnter(navItemContainer!);
|
||||
});
|
||||
|
||||
// 4. Assert dropdown is open and children are rendered
|
||||
expect(screen.getByText('Kabelbau')).toBeTruthy();
|
||||
expect(screen.getByText('Bohrtechnik')).toBeTruthy();
|
||||
|
||||
// 5. Simulate page navigation by changing the path
|
||||
currentPath = '/de/leistungen/kabelbau';
|
||||
|
||||
act(() => {
|
||||
// Trigger pathname hook change and force rerender
|
||||
rerender(<Header navLinks={mockNavLinks} />);
|
||||
});
|
||||
|
||||
// 6. Assert dropdown is closed (children are not in the document)
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('Kabelbau')).toBeNull();
|
||||
expect(screen.queryByText('Bohrtechnik')).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -164,6 +164,10 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
function NavItem({ link, currentLocale, pathname, isSolidMode }: { link: NavLink, currentLocale: string, pathname: string, isSolidMode: boolean }) {
|
||||
const [isHovered, setIsHovered] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
setIsHovered(false);
|
||||
}, [pathname]);
|
||||
|
||||
const mappedUrl = link.url.startsWith('/') && !link.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${link.url}`
|
||||
: link.url;
|
||||
|
||||
Reference in New Issue
Block a user