Skip to content
Snippets Groups Projects
Commit b81ef7cc authored by mateuszmiko's avatar mateuszmiko
Browse files

feat(drawer): add drawer functionality to store

parent 7ec0b7ea
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!19feat(drawer): add drawer functionality to store
Showing
with 234 additions and 68 deletions
......@@ -63,7 +63,8 @@
"**/*{.,_}{test,spec}.{ts,tsx}", // tests where the extension or filename suffix denotes that it is a test
"**/jest.config.ts", // jest config
"**/jest.setup.ts", // jest setup
"**/setupTests.ts"
"**/setupTests.ts",
"src/utils/*.{ts,tsx}"
],
"optionalDependencies": false,
"peerDependencies": false,
......
export const MapNavigation = (): JSX.Element => <div className="h-10 w-full bg-slate-200">.</div>;
import { Button } from '@/shared/Button';
export const MapNavigation = (): JSX.Element => (
<div className="h-10 w-full bg-white-pearl shadow-map-navigation-bar">
{/* TODO: Button is temporary until we add tabs */}
<Button className="h-10 bg-[#EBF4FF]" variantStyles="secondary">
Main map
</Button>
</div>
);
import { screen, render, RenderResult } from '@testing-library/react';
import { RenderResult, screen } from '@testing-library/react';
import { renderComponentWithProvider } from '@/utils/renderComponentWithProvider';
import { NavBar } from './NavBar.component';
const renderComponent = (): RenderResult => render(<NavBar />);
const renderComponent = (): RenderResult => renderComponentWithProvider(<NavBar />);
describe('NavBar - component', () => {
it('Should contain navigation buttons and logos with powered by info', () => {
......
......@@ -2,34 +2,56 @@ import Image from 'next/image';
import { IconButton } from '@/shared/IconButton';
import logoImg from '@/assets/images/logo.png';
import luxembourgLogoImg from '@/assets/images/luxembourg-logo.png';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { openDrawer } from '@/redux/drawer/drawer.slice';
export const NavBar = (): JSX.Element => (
<div className="flex min-h-full w-[88px] flex-col items-center justify-between bg-cultured py-8">
<div data-testid="nav-buttons">
<div className="mb-8 flex flex-col gap-[10px]">
<IconButton icon="info" />
<IconButton icon="page" />
<IconButton icon="plugin" />
<IconButton icon="export" />
</div>
<div className="flex flex-col gap-[10px]">
<IconButton icon="admin" />
<IconButton icon="legend" />
export const NavBar = (): JSX.Element => {
const dispatch = useAppDispatch();
const openDrawerInfo = (): void => {
dispatch(openDrawer('project-info'));
};
const openDrawerPlugins = (): void => {
dispatch(openDrawer('plugins'));
};
const openDrawerExport = (): void => {
dispatch(openDrawer('export'));
};
const openDrawerLegend = (): void => {
dispatch(openDrawer('legend'));
};
return (
<div className="flex min-h-full w-[88px] flex-col items-center justify-between bg-cultured py-8">
<div data-testid="nav-buttons">
<div className="mb-8 flex flex-col gap-[10px]">
<IconButton icon="info" onClick={openDrawerInfo} />
<IconButton icon="page" />
<IconButton icon="plugin" onClick={openDrawerPlugins} />
<IconButton icon="export" onClick={openDrawerExport} />
</div>
<div className="flex flex-col gap-[10px]">
<IconButton icon="admin" />
<IconButton icon="legend" onClick={openDrawerLegend} />
</div>
</div>
</div>
<div className="flex flex-col items-center gap-[20px]" data-testid="nav-logos-and-powered-by">
<Image
className="rounded rounded-e rounded-s bg-white-pearl pb-[7px]"
src={luxembourgLogoImg}
alt="luxembourg logo"
height={41}
width={48}
/>
<Image src={logoImg} alt="logo" height={48} width={48} />
<span className="h-16 w-14 text-center text-[8px] leading-4">
Powered by: MINERVA Platform (v16.0.8)
</span>
<div className="flex flex-col items-center gap-[20px]" data-testid="nav-logos-and-powered-by">
<Image
className="rounded rounded-e rounded-s bg-white-pearl pb-[7px]"
src={luxembourgLogoImg}
alt="luxembourg logo"
height={41}
width={48}
/>
<Image src={logoImg} alt="logo" height={48} width={48} />
<span className="h-16 w-14 text-center text-[8px] leading-4">
Powered by: MINERVA Platform (v16.0.8)
</span>
</div>
</div>
</div>
);
);
};
......@@ -3,7 +3,7 @@ import { UserAvatar } from '@/components/FunctionalArea/TopBar/UserAvatar';
import { Button } from '@/shared/Button';
export const TopBar = (): JSX.Element => (
<div className="flex h-16 w-full flex-row items-center justify-between bg-white py-4 pl-7 pr-6">
<div className="flex h-16 w-full flex-row items-center justify-between border-b border-font-500 border-opacity-[0.12] bg-white py-4 pl-7 pr-6">
<div className="flex flex-row items-center">
<UserAvatar />
<SearchBar />
......
import { screen, render, RenderResult } from '@testing-library/react';
import { screen, fireEvent, type RenderResult } from '@testing-library/react';
import { renderComponentWithProvider } from '@/utils/renderComponentWithProvider';
import { Drawer } from './Drawer.component';
const renderComponent = (): RenderResult => render(<Drawer />);
const renderComponent = (): RenderResult => renderComponentWithProvider(<Drawer />);
describe('Drawer - component', () => {
it('should render Drawer', () => {
......@@ -9,4 +10,14 @@ describe('Drawer - component', () => {
expect(screen.getByRole('drawer')).toBeInTheDocument();
});
it('should close Drawer', async () => {
renderComponent();
const button = screen.getByRole('close-drawer-button');
await fireEvent.click(button);
expect(screen.getByRole('drawer')).not.toHaveClass('translate-x-0');
});
});
import { useState } from 'react';
import { Button } from '@/shared/Button';
import { twMerge } from 'tailwind-merge';
import { IconButton } from '@/shared/IconButton';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { closeDrawer } from '@/redux/drawer/drawer.slice';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { drawerDataSelector } from '@/redux/drawer/drawer.selectors';
import {
CLOSE_BUTTON_ROLE,
DRAWER_ROLE,
SOURCE_FROM_DRAWER,
} from '@/components/Map/Drawer/Drawer.constants';
const drawerRole = 'drawer';
const closeButtonRole = 'close-drawer-button';
export const Drawer = (): JSX.Element => {
const dispatch = useAppDispatch();
const drawerData = useAppSelector(drawerDataSelector);
const { open } = drawerData;
export const Drawer = (): JSX.Element | null => {
const [open, setOpenDrawer] = useState(false);
const handleCloseDrawer = (): void => {
// eslint-disable-next-line prefer-template
dispatch(closeDrawer(SOURCE_FROM_DRAWER));
};
return (
<>
<Button
className="peer absolute left-[100px] top-[110px] z-10"
onClick={(): void => setOpenDrawer(true)}
>
Open Drawer
</Button>
<div
className={twMerge(
'absolute left-[88px] top-[104px] z-10 h-calc-drawer w-[432px] -translate-x-full transform bg-white-pearl text-font-500 transition-all duration-500',
open && 'translate-x-0',
)}
role={drawerRole}
>
<div className="flex items-center justify-between border-b border-b-divide px-6 py-8 text-xl">
<div>
<span className="font-normal">Search: </span>
<span className="font-semibold">NADH</span>
</div>
<IconButton
className="bg-white-pearl"
classNameIcon="fill-font-500"
icon="close"
role={closeButtonRole}
onClick={(): void => setOpenDrawer(false)}
/>
<div
className={twMerge(
'absolute left-[88px] top-[104px] z-10 h-calc-drawer w-[432px] -translate-x-full transform bg-white-pearl text-font-500 transition-all duration-500',
open && 'translate-x-0',
)}
role={DRAWER_ROLE}
>
<div className="flex items-center justify-between border-b border-b-divide px-6 py-8 text-xl">
<div>
<span className="font-normal">Search: </span>
<span className="font-semibold">NADH</span>
</div>
<IconButton
className="bg-white-pearl"
classNameIcon="fill-font-500"
icon="close"
role={CLOSE_BUTTON_ROLE}
onClick={handleCloseDrawer}
/>
</div>
</>
</div>
);
};
export const DRAWER_ROLE = 'drawer';
export const CLOSE_BUTTON_ROLE = 'close-drawer-button';
export const SOURCE_FROM_DRAWER = 'search';
import { AnyAction } from '@reduxjs/toolkit';
import * as toolkitRaw from '@reduxjs/toolkit';
import type { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore';
import drawerReducer, { openDrawer, closeDrawer } from './drawer.slice';
import type { DrawerState } from './drawer.types';
const INITIAL_STATE: DrawerState = {
open: false,
pathName: 'none',
};
type SliceReducerType = ToolkitStore<
{
drawer: DrawerState;
},
AnyAction
>;
const createStoreInstanceUsingSliceReducer = (): SliceReducerType =>
toolkitRaw.configureStore({
reducer: {
drawer: drawerReducer,
},
});
describe('drawer reducer', () => {
let store = {} as SliceReducerType;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer();
});
it('should match initial state', () => {
const action = { type: 'unknown' };
expect(drawerReducer(undefined, action)).toEqual(INITIAL_STATE);
});
it('should update the store when you click a project info button on the nav bar', async () => {
const { type } = await store.dispatch(openDrawer('project-info'));
const { open, pathName } = store.getState().drawer;
expect(type).toBe('drawer/openDrawer');
expect(open).toBe(true);
expect(pathName).toEqual('project-info');
});
it('should update the store when you click the close button on the drawer', async () => {
const { type } = await store.dispatch(closeDrawer('project-info'));
const { open, pathName } = store.getState().drawer;
expect(type).toBe('drawer/closeDrawer');
expect(open).toBe(false);
expect(pathName).toEqual('project-info');
});
it.skip('should update the store when you type in the search', async () => {
// TODO
});
});
import { PayloadAction } from '@reduxjs/toolkit';
import { DrawerState } from '@/redux/drawer/drawer.types';
import { PathName } from '@/types/pathName';
export const openDrawerReducer = (state: DrawerState, action: PayloadAction<PathName>): void => {
state.open = true;
state.pathName = action.payload;
};
export const closeDrawerReducer = (state: DrawerState, action: PayloadAction<PathName>): void => {
state.open = false;
state.pathName = action.payload;
};
import { createSelector } from '@reduxjs/toolkit';
import { rootSelector } from '@/redux/root/root.selectors';
export const drawerDataSelector = createSelector(rootSelector, state => state.drawer);
import { createSlice } from '@reduxjs/toolkit';
import { DrawerState } from '@/redux/drawer/drawer.types';
import { openDrawerReducer, closeDrawerReducer } from './drawer.reducers';
const initialState: DrawerState = {
open: false,
pathName: 'none',
};
const drawerSlice = createSlice({
name: 'drawer',
initialState,
reducers: {
openDrawer: openDrawerReducer,
closeDrawer: closeDrawerReducer,
},
});
export const { openDrawer, closeDrawer } = drawerSlice.actions;
export default drawerSlice.reducer;
export type DrawerState = {
open: boolean;
pathName: 'none' | 'search' | 'project-info' | 'plugins' | 'export' | 'legend';
};
import type { RootState } from '@/redux/store';
export const rootSelector = (state: RootState): RootState => state;
import { configureStore } from '@reduxjs/toolkit';
import searchReducer from '@/redux/search/search.slice';
import projectSlice from '@/redux/project/project.slice';
import drawerReducer from '@/redux/drawer/drawer.slice';
export const store = configureStore({
reducer: {
search: searchReducer,
project: projectSlice,
drawer: drawerReducer,
},
devTools: true,
});
......
export type PathName = 'none' | 'search' | 'project-info' | 'plugins' | 'export' | 'legend';
import { RenderResult, render } from '@testing-library/react';
import { AppWrapper } from '@/components/AppWrapper';
import type { ReactNode } from 'react';
export const renderComponentWithProvider = (children: ReactNode): RenderResult =>
render(<AppWrapper>{children}</AppWrapper>);
......@@ -29,6 +29,9 @@ const config: Config = {
height: {
'calc-drawer': 'calc(100% - 104px)',
},
boxShadow: {
'map-navigation-bar': '4px 8px 32px 0px rgba(0, 0, 0, 0.12)',
},
},
fontFamily: {
manrope: ['var(--font-manrope)'],
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment