Skip to content

Commit

Permalink
Feat/appbar (#31)
Browse files Browse the repository at this point in the history
* fix typo

* add appbar component

* implement accountPopover component

* remove

* add test for sidebar & appbar

* add test for AccountPopover
  • Loading branch information
mehdi-torabiv authored Jul 11, 2024
1 parent 0f803dd commit f966c24
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 2 deletions.
36 changes: 36 additions & 0 deletions src/components/layouts/AccountPopover.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import AccountPopover from './AccountPopover';

describe('AccountPopover', () => {
it('should render the AccountPopover component', () => {
render(<AccountPopover />);

const iconButton = screen.getByTestId('account-popover-button');
expect(iconButton).toBeInTheDocument();
});

it('should open the popover when icon button is clicked', () => {
render(<AccountPopover />);

const iconButton = screen.getByTestId('account-popover-button');
fireEvent.click(iconButton);

const logoutMenuItem = screen.getByText('Logout');
expect(logoutMenuItem).toBeVisible();
});

it('should log out the user when logout menu item is clicked', () => {
const consoleSpy = vi.spyOn(console, 'log');
render(<AccountPopover />);

const iconButton = screen.getByTestId('account-popover-button');
fireEvent.click(iconButton);

const logoutMenuItem = screen.getByText('Logout');
fireEvent.click(logoutMenuItem);

expect(consoleSpy).toHaveBeenCalledWith('User logged out');
consoleSpy.mockRestore();
});
});
93 changes: 93 additions & 0 deletions src/components/layouts/AccountPopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useState, MouseEvent } from 'react';
import {
Avatar,
Popover,
MenuItem,
IconButton,
Typography,
} from '@mui/material';
import { alpha } from '@mui/material/styles';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import LogoutIcon from '@mui/icons-material/Logout';

function AccountPopover() {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);

const handleOpen = (event: MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};

const handleClose = () => {
setAnchorEl(null);
};

const handleLogout = () => {
console.log('User logged out');
handleClose();
};

return (
<>
<IconButton
onClick={handleOpen}
data-testid="account-popover-button"
sx={{
width: 40,
height: 40,
background: (theme) => alpha(theme.palette.grey[500], 0.08),
...(anchorEl && {
background: (theme) =>
`linear-gradient(135deg, ${theme.palette.primary.light} 0%, ${theme.palette.primary.main} 100%)`,
}),
}}
>
<Avatar
sx={{
width: 36,
height: 36,
border: (theme) => `solid 2px ${theme.palette.background.default}`,
}}
>
<AccountCircleIcon
sx={{
width: 36,
height: 36,
}}
/>
</Avatar>
</IconButton>

<Popover
open={Boolean(anchorEl)}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
>
<MenuItem
sx={{
typography: 'body2',
color: 'error.main',
pb: 1,
':focus': {
backgroundColor: 'error.light',
color: 'contrastText',
},
':active': {
backgroundColor: 'error.light',
color: 'white',
},
}}
onClick={handleLogout}
>
<Typography variant="body2">
<LogoutIcon sx={{ mr: 2 }} />
Logout
</Typography>
</MenuItem>
</Popover>
</>
);
}

export default AccountPopover;
37 changes: 37 additions & 0 deletions src/components/layouts/AppbarApp.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { IconButton, Avatar } from '@mui/material';
import AppbarApp from './AppbarApp';

vi.mock('./AccountPopover', () => ({
default: () => (
<div>
<IconButton data-testid="account-popover-button">
<Avatar />
</IconButton>
<div data-testid="account-popover">AccountPopover</div>
</div>
),
}));

describe('AppbarApp', () => {
it('should render the AppbarApp component', () => {
render(<AppbarApp />);

const appBar = screen.getByTestId('Appbar');
expect(appBar).toBeInTheDocument();

const accountPopover = screen.getByTestId('account-popover');
expect(accountPopover).toBeInTheDocument();
});

it('should open AccountPopover on icon button click', () => {
render(<AppbarApp />);

const iconButton = screen.getByTestId('account-popover-button');
fireEvent.click(iconButton);

const accountPopover = screen.getByTestId('account-popover');
expect(accountPopover).toBeVisible();
});
});
35 changes: 35 additions & 0 deletions src/components/layouts/AppbarApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { AppBar, Box, Toolbar } from '@mui/material';
import AccountPopover from './AccountPopover';

function AppbarApp() {
return (
<Box>
<AppBar
data-testid="Appbar"
position="static"
elevation={0}
variant="outlined"
color="inherit"
sx={{
borderLeft: 0,
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',
backgroundColor: 'crystal.main',
}}
>
<Toolbar>
<Box sx={{ flexGrow: 1 }} />
<Box
sx={{
display: 'flex',
justifyContent: 'flex-end',
}}
>
<AccountPopover />
</Box>
</Toolbar>
</AppBar>
</Box>
);
}

export default AppbarApp;
56 changes: 56 additions & 0 deletions src/components/layouts/SidebarApp.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { BrowserRouter } from 'react-router-dom';
import SidebarApp from './SidebarApp';
import { SIDEBAR_MENU } from '../../libs/constants';

// Mock the constants
vi.mock('../../libs/constants', () => ({
DRAWER_WIDTH: 240,
SIDEBAR_MENU: [
{
title: 'Home',
path: '/home',
icon: vi.fn(() => <div data-testid="home-icon" />),
},
{
title: 'Settings',
path: '/settings',
icon: vi.fn(() => <div data-testid="settings-icon" />),
},
],
}));

describe('SidebarApp', () => {
it('should render the SidebarApp component', () => {
render(
<BrowserRouter>
<SidebarApp />
</BrowserRouter>
);

const drawer = screen.getByTestId('drawer_app');
expect(drawer).toBeInTheDocument();

const logo = screen.getByText('LOGO');
expect(logo).toBeInTheDocument();

SIDEBAR_MENU.forEach((item) => {
const menuItem = screen.getByText(item.title);
expect(menuItem).toBeInTheDocument();
});
});

it('should navigate to the correct route on menu item click', () => {
render(
<BrowserRouter>
<SidebarApp />
</BrowserRouter>
);

const homeMenuItem = screen.getByText('Home');
fireEvent.click(homeMenuItem);

expect(window.location.pathname).toBe('/home');
});
});
1 change: 1 addition & 0 deletions src/components/layouts/SidebarApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function SidebarApp() {
return (
<Box display="flex">
<Drawer
data-testid="drawer_app"
variant="permanent"
sx={{
width: DRAWER_WIDTH,
Expand Down
2 changes: 2 additions & 0 deletions src/layouts/DefaultLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Box } from '@mui/material';
import { Outlet } from 'react-router-dom';
import SidebarApp from '../components/layouts/SidebarApp';
import AppbarApp from '../components/layouts/AppbarApp';

function DefaultLayout() {
return (
Expand All @@ -14,6 +15,7 @@ function DefaultLayout() {
overflowX: 'hidden',
}}
>
<AppbarApp />
<Box
component="main"
sx={{
Expand Down
2 changes: 1 addition & 1 deletion src/libs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const SIDEBAR_MENU: MenuItem[] = [
},
{
title: 'Identifiers',
path: '/idenifiers',
path: '/identifiers',
icon: FingerprintIcon,
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/router/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const router = createBrowserRouter([
index: true,
},
{
path: '/idenifiers',
path: '/identifiers',
element: <Identifiers />,
},
{
Expand Down

0 comments on commit f966c24

Please sign in to comment.