+export type PaymentsPageProps = DetailedHTMLProps
, HTMLDivElement>;
+const PaymentsPage: FC = (props) => (
+
Payments Page
);
diff --git a/src/pages/PaymentsPage/ui/PaymentsPageToolbar.ui.tsx b/src/pages/PaymentsPage/ui/PaymentsPageToolbar.ui.tsx
index 0a844ca..472229f 100644
--- a/src/pages/PaymentsPage/ui/PaymentsPageToolbar.ui.tsx
+++ b/src/pages/PaymentsPage/ui/PaymentsPageToolbar.ui.tsx
@@ -1,10 +1,12 @@
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
+import type { ToolbarProps } from '@mui/material/Toolbar/Toolbar';
import type { FC } from 'react';
-const PaymentsPageToolbar: FC = () => (
-
+export type PaymentsPageToolbarProps = ToolbarProps;
+const PaymentsPageToolbar: FC = (props) => (
+
Payments Page Toolbar
diff --git a/src/pages/SchedulePage/ui/SchedulePage.spec.tsx b/src/pages/SchedulePage/ui/SchedulePage.spec.tsx
new file mode 100644
index 0000000..dfb306d
--- /dev/null
+++ b/src/pages/SchedulePage/ui/SchedulePage.spec.tsx
@@ -0,0 +1,31 @@
+import { render, screen } from '@testing-library/react';
+
+import { SchedulePage, SchedulePageToolbar } from 'pages/SchedulePage';
+
+describe('Test SchedulePage', () => {
+ const setup = () => render(
+ ,
+ );
+
+ test('should render correctly', () => {
+ setup();
+
+ const pageElem = screen.getByTestId('schedule-page');
+ expect(pageElem).toBeVisible();
+ expect(pageElem).toHaveTextContent('Schedule Page');
+ });
+});
+
+describe('Test SchedulePageToolabr', () => {
+ const setup = () => render(
+ ,
+ );
+
+ test('should render correctly', () => {
+ setup();
+
+ const toolbarElem = screen.getByTestId('schedule-page-toolbar');
+ expect(toolbarElem).toBeVisible();
+ expect(toolbarElem).toHaveTextContent('Schedule Page Toolbar');
+ });
+});
diff --git a/src/pages/SchedulePage/ui/SchedulePage.ui.tsx b/src/pages/SchedulePage/ui/SchedulePage.ui.tsx
index 4a6c82a..31a5d2f 100644
--- a/src/pages/SchedulePage/ui/SchedulePage.ui.tsx
+++ b/src/pages/SchedulePage/ui/SchedulePage.ui.tsx
@@ -1,7 +1,8 @@
-import type { FC } from 'react';
+import type { DetailedHTMLProps, FC, HTMLAttributes } from 'react';
-const SchedulePage: FC = () => (
-
+export type SchedulePageProps = DetailedHTMLProps
, HTMLDivElement>;
+const SchedulePage: FC = (props) => (
+
Schedule Page
);
diff --git a/src/pages/SchedulePage/ui/SchedulePageToolbar.ui.tsx b/src/pages/SchedulePage/ui/SchedulePageToolbar.ui.tsx
index 6144077..38f5013 100644
--- a/src/pages/SchedulePage/ui/SchedulePageToolbar.ui.tsx
+++ b/src/pages/SchedulePage/ui/SchedulePageToolbar.ui.tsx
@@ -1,10 +1,12 @@
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
+import type { ToolbarProps } from '@mui/material/Toolbar/Toolbar';
import type { FC } from 'react';
-const SchedulePageToolbar: FC = () => (
-
+type SchedulePageToolbarProps = ToolbarProps;
+const SchedulePageToolbar: FC = (props) => (
+
Schedule Page Toolbar
diff --git a/src/widgets/AsideMenuLayout/ui/AsideMenuLayout.spec.tsx b/src/widgets/AsideMenuLayout/ui/AsideMenuLayout.spec.tsx
new file mode 100644
index 0000000..f349181
--- /dev/null
+++ b/src/widgets/AsideMenuLayout/ui/AsideMenuLayout.spec.tsx
@@ -0,0 +1,116 @@
+import { renderWithProvider } from '__spec__/custon-renders';
+
+import PieChartIcon from '@mui/icons-material/PieChart';
+import { fireEvent, screen } from '@testing-library/react';
+
+import AsideMenuLayout from './AsideMenuLayout.ui';
+
+const toggleSwitcher = (testId = 'drawer-switcher') => {
+ const switcherElem = screen.getByTestId(testId);
+ fireEvent.click(switcherElem);
+};
+
+describe('Test AsideMenuLayout', () => {
+ const setup = () => {
+ const Content = () => (
+
+ Test Content
+
+ );
+
+ const Toolbar = () => (
+
+ Test Toolbar
+
+ );
+
+ const pages = [
+ {
+ icon: ,
+ title: 'Test Route',
+ path: '/test-route',
+ },
+ {
+ icon: ,
+ title: 'Another Test Route',
+ path: '/another-test-route',
+ },
+ ];
+
+ return renderWithProvider(
+ }
+ data-testid="aside-menu-layout"
+ DrawerSwitcherProps={{
+ // @ts-ignore
+ 'data-testid': 'drawer-switcher',
+ }}
+ pages={pages}
+ Toolbar={}
+ />,
+ );
+ };
+
+ test('should render correctly', () => {
+ setup();
+
+ const layoutElem = screen.getByTestId('aside-menu-layout');
+ expect(layoutElem).toBeVisible();
+ });
+
+ test('should render toolbar', () => {
+ setup();
+
+ const toolbarElem = screen.getByText('Test Toolbar');
+ expect(toolbarElem).toBeVisible();
+ });
+
+ test('should render content', () => {
+ setup();
+
+ const toolbarElem = screen.getByText('Test Content');
+ expect(toolbarElem).toBeVisible();
+ });
+
+ test('should render navigation routes', () => {
+ setup();
+
+ const linkElem = screen.getByText('Test Route');
+ expect(linkElem).toBeVisible();
+
+ const anotherLinkElem = screen.getByText('Another Test Route');
+ expect(anotherLinkElem).toBeVisible();
+ });
+
+ describe('Test switch open/close drawer', () => {
+ test('should have correct close icon', () => {
+ setup();
+
+ const closeIconElem = screen.getByTestId('ChevronLeftIcon');
+ expect(closeIconElem).toBeVisible();
+ });
+
+ test('should have correct open icon', () => {
+ setup();
+ toggleSwitcher();
+
+ const openIconElem = screen.getByTestId('MenuIcon');
+ expect(openIconElem).toBeVisible();
+ });
+
+ test('should show link text', () => {
+ setup();
+
+ const linkTextElem = screen.getByText('Test Route');
+ expect(linkTextElem).toBeVisible();
+ });
+
+ test('should hide link text', () => {
+ setup();
+ toggleSwitcher();
+
+ const linkTextElem = screen.queryByText('Test Route');
+ expect(linkTextElem).not.toBeVisible();
+ });
+ });
+});
diff --git a/src/widgets/AsideMenuLayout/ui/AsideMenuLayout.ui.tsx b/src/widgets/AsideMenuLayout/ui/AsideMenuLayout.ui.tsx
index 51f9a01..e94bc10 100644
--- a/src/widgets/AsideMenuLayout/ui/AsideMenuLayout.ui.tsx
+++ b/src/widgets/AsideMenuLayout/ui/AsideMenuLayout.ui.tsx
@@ -10,23 +10,32 @@ import AppBar from './components/AppBar';
import Drawer from './components/Drawer';
import DrawerHeader from './components/DrawerMenu';
import DrawerSwitcher from './components/DrawerSwitcher';
-import MenuItem from './components/MenuItem';
+import MenuItem from './components/MenuItem/MenuItem.ui';
-import type { MenuItemProps } from './components/MenuItem';
-import type { FC, ReactNode } from 'react';
+import type { MenuItemProps } from './components/MenuItem/MenuItem.ui';
+import type { BoxProps } from '@mui/material/Box/Box';
+import type {
+ FC,
+ ReactNode,
+ DetailedHTMLProps,
+ HTMLAttributes,
+} from 'react';
const drawerWidth = 250;
-type AsideMenuLayoutProps = {
+type AsideMenuLayoutProps = BoxProps & {
pages: MenuItemProps['page'][];
Content: ReactNode;
Toolbar?: ReactNode;
+ DrawerSwitcherProps?: DetailedHTMLProps, HTMLDivElement>;
};
const AsideMenuLayout: FC = (props) => {
const {
pages,
Content,
Toolbar,
+ DrawerSwitcherProps,
+ ...restProps
} = props;
const [isOpen, setIsOpen] = useState(true);
@@ -39,7 +48,7 @@ const AsideMenuLayout: FC = (props) => {
};
return (
-
+
= (props) => {
{ isOpen ? : }
diff --git a/src/widgets/AsideMenuLayout/ui/components/AppBar/index.tsx b/src/widgets/AsideMenuLayout/ui/components/AppBar/index.tsx
index 98e9d21..582417b 100644
--- a/src/widgets/AsideMenuLayout/ui/components/AppBar/index.tsx
+++ b/src/widgets/AsideMenuLayout/ui/components/AppBar/index.tsx
@@ -8,7 +8,7 @@ type AppBarProps = MuiAppBarProps & {
drawerWidth: number;
};
-const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => prop !== 'open' })(
+const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => !['open', 'drawerWidth'].includes(prop as string) })(
({ theme, open, drawerWidth }) => ({
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
diff --git a/src/widgets/AsideMenuLayout/ui/components/Drawer/index.tsx b/src/widgets/AsideMenuLayout/ui/components/Drawer/index.tsx
index 29e654d..f09dbcd 100644
--- a/src/widgets/AsideMenuLayout/ui/components/Drawer/index.tsx
+++ b/src/widgets/AsideMenuLayout/ui/components/Drawer/index.tsx
@@ -24,7 +24,7 @@ const closedMixin = (theme: Theme): CSSObject => ({
width: `calc(${theme.spacing(8)} + 1px)`,
});
-const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
+const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => !['open', 'drawerWidth'].includes(prop as string) })(
({ theme, open, drawerWidth }) => ({
width: drawerWidth,
flexShrink: 0,
diff --git a/src/widgets/AsideMenuLayout/ui/components/MenuItem/MenuItem.spec.tsx b/src/widgets/AsideMenuLayout/ui/components/MenuItem/MenuItem.spec.tsx
new file mode 100644
index 0000000..b30f37d
--- /dev/null
+++ b/src/widgets/AsideMenuLayout/ui/components/MenuItem/MenuItem.spec.tsx
@@ -0,0 +1,70 @@
+import { renderWithProvider } from '__spec__/custon-renders';
+
+import PieChartIcon from '@mui/icons-material/PieChart';
+import { screen } from '@testing-library/react';
+
+import MenuItem from './MenuItem.ui';
+
+import type { MenuItemProps } from './MenuItem.ui';
+
+describe('MenuItem', () => {
+ const setup = (props?: Partial) => renderWithProvider(
+ ,
+ title: 'Test Route',
+ path: '/test-route',
+ }}
+ {...props}
+ />,
+ );
+
+ test('should render', () => {
+ setup();
+
+ const itemElem = screen.getByTestId('menu-item');
+ expect(itemElem).toBeVisible();
+ });
+
+ test('should render correct text', () => {
+ setup();
+
+ const textElem = screen.getByText('Test Route');
+ expect(textElem).toBeVisible();
+ });
+
+ test('should render correct icon', () => {
+ setup();
+
+ const iconElem = screen.getByTestId('PieChartIcon');
+ expect(iconElem).toBeVisible();
+ });
+
+ test('should have correct href', () => {
+ setup();
+
+ const itemElem = screen.getByTestId('menu-item-link');
+ expect(itemElem).toHaveAttribute('href', '/test-route');
+ });
+
+ describe('Test closed MenuItem', () => {
+ test('should not render text if closed', () => {
+ setup({ isOpen: false });
+
+ const textElem = screen.getByText('Test Route');
+ expect(textElem).not.toBeVisible();
+ });
+
+ test('should render correct icon if closed', () => {
+ setup({ isOpen: false });
+
+ const iconElem = screen.getByTestId('PieChartIcon');
+ expect(iconElem).toBeVisible();
+ });
+ });
+});
diff --git a/src/widgets/AsideMenuLayout/ui/components/MenuItem/index.tsx b/src/widgets/AsideMenuLayout/ui/components/MenuItem/MenuItem.ui.tsx
similarity index 82%
rename from src/widgets/AsideMenuLayout/ui/components/MenuItem/index.tsx
rename to src/widgets/AsideMenuLayout/ui/components/MenuItem/MenuItem.ui.tsx
index 58fe27d..b46e5db 100644
--- a/src/widgets/AsideMenuLayout/ui/components/MenuItem/index.tsx
+++ b/src/widgets/AsideMenuLayout/ui/components/MenuItem/MenuItem.ui.tsx
@@ -6,7 +6,9 @@ import { styled, useTheme } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import { NavLink as RRDNavLink } from 'react-router-dom';
+import type { ListItemProps } from '@mui/material/ListItem/ListItem';
import type { FC, ReactNode } from 'react';
+import type { NavLinkProps } from 'react-router-dom';
const NavLink = styled(RRDNavLink)(({ theme }) => ({
textDecoration: 'none',
@@ -14,25 +16,28 @@ const NavLink = styled(RRDNavLink)(({ theme }) => ({
width: '100%',
}));
-export type MenuItemProps = {
+export type MenuItemProps = ListItemProps & {
page: {
icon: ReactNode;
title: string;
path: string;
};
- isOpen: boolean;
+ isOpen?: boolean;
+ NavLinkProps?: NavLinkProps;
};
const MenuItem: FC = (props) => {
const {
page,
- isOpen,
+ isOpen = true,
+ NavLinkProps,
+ ...restProps
} = props;
const theme = useTheme();
return (
-
-
+
+
{({ isActive }) => (