Implemented responsive layout system with: - Header: Top navigation with profile menu and mobile hamburger - Sidebar: Desktop-only navigation sidebar (lg and above) - Breadcrumbs: Auto-generated navigation breadcrumbs - Layout: Main wrapper component with configurable options Features: - Responsive design (mobile, tablet, desktop) - Active route highlighting - User profile integration via auth store - Click-outside dropdown closing - Comprehensive test coverage (49 tests passing) Updated (main) layout to use new Layout component system. Updated dashboard page to work with new layout structure. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
222 lines
5.4 KiB
TypeScript
222 lines
5.4 KiB
TypeScript
import { render, screen } from '@testing-library/react';
|
|
import { Layout } from '../Layout';
|
|
import { useAuthStore } from '@/stores/authStore';
|
|
import { usePathname } from 'next/navigation';
|
|
|
|
// Mock child components
|
|
jest.mock('../Header', () => ({
|
|
Header: () => <div data-testid="header">Header</div>,
|
|
}));
|
|
|
|
jest.mock('../Sidebar', () => ({
|
|
Sidebar: () => <div data-testid="sidebar">Sidebar</div>,
|
|
}));
|
|
|
|
jest.mock('../Breadcrumbs', () => ({
|
|
Breadcrumbs: () => <div data-testid="breadcrumbs">Breadcrumbs</div>,
|
|
}));
|
|
|
|
// Mock Next.js navigation
|
|
jest.mock('next/navigation', () => ({
|
|
usePathname: jest.fn(),
|
|
}));
|
|
|
|
// Mock auth store
|
|
jest.mock('@/stores/authStore', () => ({
|
|
useAuthStore: jest.fn(),
|
|
}));
|
|
|
|
describe('Layout Component', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
(usePathname as jest.Mock).mockReturnValue('/dashboard');
|
|
(useAuthStore as unknown as jest.Mock).mockReturnValue({
|
|
user: { user_id: 1, email: 'test@example.com' },
|
|
logout: jest.fn(),
|
|
});
|
|
});
|
|
|
|
it('renders children content', () => {
|
|
render(
|
|
<Layout>
|
|
<div>Test Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.getByText('Test Content')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders header by default', () => {
|
|
render(
|
|
<Layout>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders sidebar by default', () => {
|
|
render(
|
|
<Layout>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.getByTestId('sidebar')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders breadcrumbs by default', () => {
|
|
render(
|
|
<Layout>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.getByTestId('breadcrumbs')).toBeInTheDocument();
|
|
});
|
|
|
|
it('hides header when showHeader is false', () => {
|
|
render(
|
|
<Layout showHeader={false}>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.queryByTestId('header')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('hides sidebar when showSidebar is false', () => {
|
|
render(
|
|
<Layout showSidebar={false}>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.queryByTestId('sidebar')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('hides breadcrumbs when showBreadcrumbs is false', () => {
|
|
render(
|
|
<Layout showBreadcrumbs={false}>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.queryByTestId('breadcrumbs')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('renders page title when provided', () => {
|
|
render(
|
|
<Layout title="Dashboard">
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.getByText('Dashboard')).toBeInTheDocument();
|
|
});
|
|
|
|
it('applies correct max-width class (7xl by default)', () => {
|
|
const { container } = render(
|
|
<Layout>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
const main = container.querySelector('main div');
|
|
expect(main).toHaveClass('max-w-7xl');
|
|
});
|
|
|
|
it('applies custom max-width class', () => {
|
|
const { container } = render(
|
|
<Layout maxWidth="4xl">
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
const main = container.querySelector('main div');
|
|
expect(main).toHaveClass('max-w-4xl');
|
|
});
|
|
|
|
it('applies full-width when maxWidth is full', () => {
|
|
const { container } = render(
|
|
<Layout maxWidth="full">
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
const main = container.querySelector('main div');
|
|
expect(main).not.toHaveClass('max-w-7xl');
|
|
expect(main).not.toHaveClass('max-w-4xl');
|
|
});
|
|
|
|
it('applies sidebar padding class to main area', () => {
|
|
const { container } = render(
|
|
<Layout showSidebar={true}>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
const mainWrapper = container.querySelector('.lg\\:pl-64');
|
|
expect(mainWrapper).toBeInTheDocument();
|
|
});
|
|
|
|
it('does not apply sidebar padding when sidebar is hidden', () => {
|
|
const { container } = render(
|
|
<Layout showSidebar={false}>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
const mainWrapper = container.querySelector('.lg\\:pl-64');
|
|
expect(mainWrapper).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('has proper background color', () => {
|
|
const { container } = render(
|
|
<Layout>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
const wrapper = container.firstChild;
|
|
expect(wrapper).toHaveClass('bg-slate-50');
|
|
});
|
|
|
|
it('renders with all features enabled', () => {
|
|
render(
|
|
<Layout
|
|
showHeader={true}
|
|
showSidebar={true}
|
|
showBreadcrumbs={true}
|
|
title="Test Page"
|
|
>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
expect(screen.getByTestId('sidebar')).toBeInTheDocument();
|
|
expect(screen.getByTestId('breadcrumbs')).toBeInTheDocument();
|
|
expect(screen.getByText('Test Page')).toBeInTheDocument();
|
|
expect(screen.getByText('Content')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders minimal layout without any chrome', () => {
|
|
render(
|
|
<Layout
|
|
showHeader={false}
|
|
showSidebar={false}
|
|
showBreadcrumbs={false}
|
|
>
|
|
<div>Content</div>
|
|
</Layout>
|
|
);
|
|
|
|
expect(screen.queryByTestId('header')).not.toBeInTheDocument();
|
|
expect(screen.queryByTestId('sidebar')).not.toBeInTheDocument();
|
|
expect(screen.queryByTestId('breadcrumbs')).not.toBeInTheDocument();
|
|
expect(screen.getByText('Content')).toBeInTheDocument();
|
|
});
|
|
});
|