Skip to content

Commit

Permalink
feat(app-menu): migrates the app menu to mui
Browse files Browse the repository at this point in the history
Migrates the AppMenu component to use Material UI Components

re #143
  • Loading branch information
anguspiv committed Mar 19, 2023
1 parent 1700118 commit ae0172f
Show file tree
Hide file tree
Showing 18 changed files with 210 additions and 343 deletions.
4 changes: 2 additions & 2 deletions src/components/layout/SideBar/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Box, Flex, Grid, GridItem } from '@chakra-ui/react';
import { useSession } from 'next-auth/react';
import AppMenu from '@components/navigation/AppMenu';
import NavLink from '@components/navigation/NavLink';
import { AppMenu } from '@components/molecules/AppMenu';
import NavLink from '@components/atoms/NavLink';

function SideBar() {
const { status } = useSession();
Expand Down
43 changes: 43 additions & 0 deletions src/components/molecules/AppMenu/AppMenu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Story } from '@storybook/react';
import { SessionProvider } from 'next-auth/react';
import { profileQuery } from '@components/molecules/AccountLink';
import { AppMenu } from './AppMenu';

export default {
title: 'Molecules/AppMenu',
component: AppMenu,
};

const Template: Story = () => {
return (
<SessionProvider session={{ expires: '' }}>
<AppMenu />
</SessionProvider>
);
};

export const Default = Template.bind({});
Default.args = {};

Default.parameters = {
apolloClient: {
mocks: [
{
request: {
query: profileQuery,
},
result: {
data: {
profile: {
firstName: 'John',
lastName: 'Doe',
avatar: {
filepath: 'https://i.pravatar.cc/300',
},
},
},
},
},
],
},
};
67 changes: 67 additions & 0 deletions src/components/molecules/AppMenu/AppMenu.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { screen, render } from '@testing-library/react';
import { useSession } from 'next-auth/react';
import { AppMenu } from './AppMenu';

jest.mock<typeof import('next/router')>('next/router', () => ({
...jest.requireActual<typeof import('next/router')>('next/router'),
useRouter: () =>
({
asPath: '/',
} as NextRouter),
}));

jest.mock<typeof import('next-auth/react')>('next-auth/react', () => ({
useSession: jest.fn(),
}));

jest.mock<typeof import('@apollo/client')>('@apollo/client', () => ({
useQuery: jest.fn().mockReturnValue({
data: { profile: { firstName: 'John', lastName: 'Doe', avatar: { filepath: 'test.jpg' } } },
}),
gql: jest.fn(),
}));

describe('<AppMenu />', () => {
const setupAppMenu = (props: object = {}, context: object = {}) => {
const { session } = context;

useSession.mockClear().mockReturnValue({ ...session });

return render(<AppMenu {...props} />);
};

it('renders the app menu', () => {
expect.assertions(1);

setupAppMenu();

expect(screen.getByRole('navigation', { name: 'account' })).toBeInTheDocument();
});

it('should render the login link when not authenticated', () => {
expect.assertions(1);

setupAppMenu({}, { session: { status: 'unauthenticated' } });

expect(screen.getByRole('link', { name: 'Login' })).toBeInTheDocument();
});

it('should render the user menu when authenticated', () => {
expect.assertions(4);

setupAppMenu({}, { session: { status: 'authenticated' } });

expect(screen.getByRole('link', { name: /John D/i })).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'John D.' })).toHaveAttribute('src', '/test.jpg');
expect(screen.getByRole('link', { name: 'Account' })).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Logout' })).toBeInTheDocument();
});

it('should render the players nav menu', () => {
expect.assertions(1);

setupAppMenu({}, { session: { status: 'authenticated' } });

expect(screen.getByRole('link', { name: 'Players' })).toBeInTheDocument();
});
});
91 changes: 91 additions & 0 deletions src/components/molecules/AppMenu/AppMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import Link from 'next/link';
import { Avatar, Box, Divider, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material';
import { gql, useQuery } from '@apollo/client';
import { useSession } from 'next-auth/react';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
import { getImageUrl } from '@utils/image';
import { getShortName } from '@utils/profile';
import PeopleAltIcon from '@mui/icons-material/PeopleAlt';
import LogoutIcon from '@mui/icons-material/Logout';

export const profileQuery = gql`
query AccountLinkQuery {
profile {
firstName
lastName
avatar {
filepath
}
}
}
`;

export function AppMenu() {
const { status } = useSession();
const { data } = useQuery(profileQuery);

const isAuthed = status === 'authenticated';
const profile = data?.profile || {};

const imgSrc = getImageUrl(profile?.avatar || {});
const label = getShortName(profile || {});

return (
<Box data-testid="app-menu">
<nav aria-label="account">
<List>
<ListItem disablePadding>
{isAuthed ? (
<ListItemButton href="/account" component={Link}>
<ListItemIcon>
<Avatar alt={label} src={imgSrc} sx={{ width: 24, height: 24 }} />
</ListItemIcon>
<ListItemText primary={label} />
</ListItemButton>
) : (
<ListItemButton href="/api/auth/signin" component={Link}>
<ListItemIcon>
<AccountCircleIcon />
</ListItemIcon>
<ListItemText primary="Login" />
</ListItemButton>
)}
</ListItem>
{isAuthed && (
<>
<ListItem disablePadding>
<ListItemButton href="/account" component={Link}>
<ListItemIcon>
<AccountCircleIcon />
</ListItemIcon>
<ListItemText primary="Account" />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton href="/api/auth/signout" component={Link} alignItems="center">
<ListItemIcon>
<LogoutIcon />
</ListItemIcon>
<ListItemText primary="Logout" />
</ListItemButton>
</ListItem>
</>
)}
</List>
</nav>
<nav aria-label="players">
<Divider />
<List>
<ListItem disablePadding>
<ListItemButton href="/players" component={Link}>
<ListItemIcon>
<PeopleAltIcon />
</ListItemIcon>
<ListItemText primary="Players" />
</ListItemButton>
</ListItem>
</List>
</nav>
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import AppMenu from './AppMenu';
import { AppMenu } from './AppMenu';

export * from './AppMenu';

Expand Down
8 changes: 4 additions & 4 deletions src/components/navigation/AppDrawer/AppDrawer.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { useSession as useSessionOrig } from 'next-auth/react';
import AppDrawer from './AppDrawer';

Expand Down Expand Up @@ -46,7 +46,7 @@ describe('<AppDrawer />', () => {

const { getByRole } = setupAppDrawer();

expect(getByRole('navigation')).toBeInTheDocument();
expect(getByRole('navigation', { name: 'account' })).toBeInTheDocument();
});

it('renders the app menu title', () => {
Expand All @@ -64,9 +64,9 @@ describe('<AppDrawer />', () => {
status: 'authenticated',
};

const { getByText } = setupAppDrawer({}, { session });
setupAppDrawer({}, { session });

expect(getByText('User')).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Account' })).toBeInTheDocument();
});

it('should render the account link', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/navigation/AppDrawer/AppDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
DrawerFooter,
} from '@chakra-ui/react';
import { useSession } from 'next-auth/react';
import AppMenu from '@components/navigation/AppMenu';
import NavLink from '../NavLink';
import { AppMenu } from '@components/molecules/AppMenu';
import NavLink from '@components/atoms/NavLink';

export interface AppDrawerProps {
isOpen?: boolean;
Expand Down
25 changes: 0 additions & 25 deletions src/components/navigation/AppMenu/AppMenu.stories.tsx

This file was deleted.

104 changes: 0 additions & 104 deletions src/components/navigation/AppMenu/AppMenu.test.tsx

This file was deleted.

0 comments on commit ae0172f

Please sign in to comment.