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

feat: search drawer stepper (MIN-97)

parent 20a88f15
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...,!33feat: search drawer stepper (MIN-97)
Showing
with 345 additions and 32 deletions
import Image from 'next/image';
import { IconButton } from '@/shared/IconButton';
import logoImg from '@/assets/images/logo.png'; import logoImg from '@/assets/images/logo.png';
import luxembourgLogoImg from '@/assets/images/luxembourg-logo.png'; import luxembourgLogoImg from '@/assets/images/luxembourg-logo.png';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { openDrawer } from '@/redux/drawer/drawer.slice'; import { openDrawer } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { IconButton } from '@/shared/IconButton';
import Image from 'next/image';
export const NavBar = (): JSX.Element => { export const NavBar = (): JSX.Element => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
......
import lensIcon from '@/assets/vectors/icons/lens.svg'; import lensIcon from '@/assets/vectors/icons/lens.svg';
import { isDrawerOpenSelector } from '@/redux/drawer/drawer.selectors'; import { isDrawerOpenSelector } from '@/redux/drawer/drawer.selectors';
import { openDrawer } from '@/redux/drawer/drawer.slice'; import { clearSearchDrawerState, openDrawer } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { import {
isPendingSearchStatusSelector, isPendingSearchStatusSelector,
...@@ -25,6 +25,7 @@ export const SearchBar = (): JSX.Element => { ...@@ -25,6 +25,7 @@ export const SearchBar = (): JSX.Element => {
const openSearchDrawerIfClosed = (): void => { const openSearchDrawerIfClosed = (): void => {
if (!isDrawerOpen) { if (!isDrawerOpen) {
dispatch(openDrawer('search')); dispatch(openDrawer('search'));
dispatch(clearSearchDrawerState());
} }
}; };
......
import { screen, render, act, fireEvent } from '@testing-library/react';
import { openDrawer } from '@/redux/drawer/drawer.slice'; import { openDrawer } from '@/redux/drawer/drawer.slice';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { StoreType } from '@/redux/store'; import { StoreType } from '@/redux/store';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { act, fireEvent, render, screen } from '@testing-library/react';
import { Drawer } from './Drawer.component'; import { Drawer } from './Drawer.component';
const renderComponent = (): { store: StoreType } => { const renderComponent = (): { store: StoreType } => {
......
import dynamic from 'next/dynamic';
import { twMerge } from 'tailwind-merge';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { DRAWER_ROLE } from '@/components/Map/Drawer/Drawer.constants'; import { DRAWER_ROLE } from '@/components/Map/Drawer/Drawer.constants';
import { drawerSelector } from '@/redux/drawer/drawer.selectors'; import { drawerSelector } from '@/redux/drawer/drawer.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import dynamic from 'next/dynamic';
import { twMerge } from 'tailwind-merge';
const SearchDrawerContent = dynamic( const SearchDrawerContent = dynamic(
async () => async () =>
......
import { render, screen } from '@testing-library/react'; import { drugsFixture } from '@/models/fixtures/drugFixtures';
import { StoreType } from '@/redux/store'; import { StoreType } from '@/redux/store';
import { Accordion } from '@/shared/Accordion';
import { import {
InitialStoreState, InitialStoreState,
getReduxWrapperWithStore, getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore'; } from '@/utils/testing/getReduxWrapperWithStore';
import { drugsFixture } from '@/models/fixtures/drugFixtures'; import { render, screen } from '@testing-library/react';
import { Accordion } from '@/shared/Accordion';
import { DrugsAccordion } from './DrugsAccordion.component'; import { DrugsAccordion } from './DrugsAccordion.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
......
import { SearchDrawerWrapper } from '@/components/Map/Drawer/SearchDrawerWrapper';
import { StoreType } from '@/redux/store';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen } from '@testing-library/react';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<SearchDrawerWrapper />
</Wrapper>,
),
{
store,
}
);
};
describe('SearchDrawerWrapper - component', () => {
it('should display the first step for search', () => {
renderComponent({
drawer: {
isOpen: true,
drawerName: 'search',
searchDrawerState: {
currentStep: 1,
selectedValue: {
name: '',
valueType: 'none',
},
},
},
});
expect(screen.getByTestId('search-first-step')).toBeInTheDocument();
});
it('should display the second step for value type bioEntity', () => {
renderComponent({
drawer: {
isOpen: true,
drawerName: 'search',
searchDrawerState: {
currentStep: 2,
selectedValue: {
model: { name: 'test model bioEntity', id: 'test-id' },
name: 'bio entity second step',
valueType: 'bioEntity',
},
},
},
});
expect(screen.getByTestId('search-second-step')).toBeInTheDocument();
});
it('should display the second step for value type chemicals', () => {
renderComponent({
drawer: {
isOpen: true,
drawerName: 'search',
searchDrawerState: {
currentStep: 2,
selectedValue: {
name: 'chemicals second step',
valueType: 'chemicals',
},
},
},
});
expect(screen.getByTestId('search-second-step')).toBeInTheDocument();
});
it('should display the third step for value type bioEntity', () => {
renderComponent({
drawer: {
isOpen: true,
drawerName: 'search',
searchDrawerState: {
currentStep: 3,
selectedValue: {
model: { name: 'test model bioEntity', id: 'test-id' },
name: 'bio entity third step',
valueType: 'bioEntity',
},
},
},
});
expect(screen.getByTestId('search-third-step')).toBeInTheDocument();
});
it('should display the third step for value type chemicals', () => {
renderComponent({
drawer: {
isOpen: true,
drawerName: 'search',
searchDrawerState: {
currentStep: 3,
selectedValue: {
name: 'chemicals third step',
valueType: 'chemicals',
},
},
},
});
expect(screen.getByTestId('search-third-step')).toBeInTheDocument();
});
});
import { STEP } from '@/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.constants';
import { BIO_ENTITY, DRUGS_CHEMICALS_MIRNA } from '@/constants';
import {
currentStepDrawerStateSelector,
valueTypeDrawerSelector,
} from '@/redux/drawer/drawer.selectors';
import { useSelector } from 'react-redux';
export const SearchDrawerWrapper = (): JSX.Element => {
const currentStep = useSelector(currentStepDrawerStateSelector);
const valueType = useSelector(valueTypeDrawerSelector);
const isBioEntityType = valueType === BIO_ENTITY;
const isChemicalsDrugsOrMirnaType = DRUGS_CHEMICALS_MIRNA.includes(valueType);
return (
<div>
{/* first step for displaying search results, drawers etc */}
{currentStep === STEP.FIRST && <div data-testid="search-first-step">The first step</div>}
{/* 2nd step for bioEntities aka content */}
{currentStep === STEP.SECOND && isBioEntityType && (
<div data-testid="search-second-step">The second step</div>
)}
{/* 2nd step for drugs,chemicals,mirna */}
{currentStep === STEP.SECOND && isChemicalsDrugsOrMirnaType && (
<div data-testid="search-second-step">The second step</div>
)}
{/* last step for bioentity */}
{currentStep === STEP.THIRD && isBioEntityType && (
<div data-testid="search-third-step">The third step</div>
)}
{/* last step for drugs,chemicals,mirna */}
{currentStep === STEP.THIRD && isChemicalsDrugsOrMirnaType && (
<div data-testid="search-third-step">The third step</div>
)}
</div>
);
};
export const STEP = {
FIRST: 1,
SECOND: 2,
THIRD: 3,
};
export { SearchDrawerWrapper } from './SearchDrawerWrapper.component';
export const BASE_API_URL = process.env.NEXT_PUBLIC_BASE_API_URL || ''; export const BASE_API_URL = process.env.NEXT_PUBLIC_BASE_API_URL || '';
export const PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID || ''; export const PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID || '';
export const ZOD_SEED = 997; export const ZOD_SEED = 997;
export const BIO_ENTITY = 'bioEntity';
export const DRUGS_CHEMICALS_MIRNA = ['drugs', 'chemicals', 'mirna'];
import { AnyAction } from '@reduxjs/toolkit';
import * as toolkitRaw from '@reduxjs/toolkit'; import * as toolkitRaw from '@reduxjs/toolkit';
import { AnyAction } from '@reduxjs/toolkit';
import type { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore'; import type { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore';
import drawerReducer, { openDrawer, closeDrawer } from './drawer.slice'; import drawerReducer, {
clearSearchDrawerState,
closeDrawer,
openDrawer,
setSearchDrawerState,
} from './drawer.slice';
import type { DrawerState } from './drawer.types'; import type { DrawerState } from './drawer.types';
const INITIAL_STATE: DrawerState = { const INITIAL_STATE: DrawerState = {
isOpen: false, isOpen: false,
drawerName: 'none', drawerName: 'none',
searchDrawerState: {
currentStep: 0,
selectedValue: {
name: '',
valueType: 'none',
},
},
}; };
type SliceReducerType = ToolkitStore< type SliceReducerType = ToolkitStore<
...@@ -54,7 +66,51 @@ describe('drawer reducer', () => { ...@@ -54,7 +66,51 @@ describe('drawer reducer', () => {
expect(drawerName).toEqual('none'); expect(drawerName).toEqual('none');
}); });
it.skip('should update the store when you type in the search', async () => { it('should update the store when you choose from the drawer chemical`s first step', async () => {
// TODO const searchDrawerData: DrawerState['searchDrawerState'] = {
currentStep: 1,
selectedValue: {
name: 'chemicals frist step',
valueType: 'chemicals',
},
};
const { type } = await store.dispatch(setSearchDrawerState(searchDrawerData));
const { searchDrawerState } = store.getState().drawer;
const { currentStep, selectedValue } = searchDrawerState;
const currentStepToBE = 1;
expect(type).toBe('drawer/setSearchDrawerState');
expect(currentStep).toBe(currentStepToBE);
expect(selectedValue).toEqual({
name: 'chemicals frist step',
valueType: 'chemicals',
});
});
it('should update the store when you clear the search drawer state', async () => {
const searchDrawerData: DrawerState['searchDrawerState'] = {
currentStep: 1,
selectedValue: {
name: 'chemicals frist step',
valueType: 'chemicals',
},
};
await store.dispatch(setSearchDrawerState(searchDrawerData));
const { type } = await store.dispatch(clearSearchDrawerState());
const { searchDrawerState } = store.getState().drawer;
const { currentStep, selectedValue } = searchDrawerState;
const currentStepToBE = 0;
expect(type).toBe('drawer/clearSearchDrawerState');
expect(currentStep).toBe(currentStepToBE);
expect(selectedValue).toEqual({
name: '',
valueType: 'none',
});
}); });
}); });
import { PayloadAction } from '@reduxjs/toolkit'; import type { DrawerState } from '@/redux/drawer/drawer.types';
import { DrawerState } from '@/redux/drawer/drawer.types'; import type { DrawerName } from '@/types/drawerName';
import { PathName } from '@/types/pathName'; import type { PayloadAction } from '@reduxjs/toolkit';
export const openDrawerReducer = (state: DrawerState, action: PayloadAction<PathName>): void => { export const openDrawerReducer = (state: DrawerState, action: PayloadAction<DrawerName>): void => {
state.isOpen = true; state.isOpen = true;
state.drawerName = action.payload; state.drawerName = action.payload;
}; };
...@@ -10,3 +10,21 @@ export const openDrawerReducer = (state: DrawerState, action: PayloadAction<Path ...@@ -10,3 +10,21 @@ export const openDrawerReducer = (state: DrawerState, action: PayloadAction<Path
export const closeDrawerReducer = (state: DrawerState): void => { export const closeDrawerReducer = (state: DrawerState): void => {
state.isOpen = false; state.isOpen = false;
}; };
export const setSearchDrawerStateReducer = (
state: DrawerState,
action: PayloadAction<DrawerState['searchDrawerState']>,
): void => {
const { currentStep, selectedValue } = action.payload;
state.searchDrawerState.currentStep = currentStep;
state.searchDrawerState.selectedValue = selectedValue;
};
export const clearSearchDrawerStateReducer = (state: DrawerState): void => {
state.searchDrawerState.currentStep = 0;
state.searchDrawerState.selectedValue = {
name: '',
valueType: 'none',
};
};
import { createSelector } from '@reduxjs/toolkit';
import { rootSelector } from '@/redux/root/root.selectors'; import { rootSelector } from '@/redux/root/root.selectors';
import { createSelector } from '@reduxjs/toolkit';
export const drawerSelector = createSelector(rootSelector, state => state.drawer); export const drawerSelector = createSelector(rootSelector, state => state.drawer);
export const isDrawerOpenSelector = createSelector(drawerSelector, state => state.isOpen); export const isDrawerOpenSelector = createSelector(drawerSelector, state => state.isOpen);
export const searchDrawerStateSelector = createSelector(
drawerSelector,
state => state.searchDrawerState,
);
export const currentStepDrawerStateSelector = createSelector(
searchDrawerStateSelector,
state => state.currentStep,
);
export const selectedValueDrawerSelector = createSelector(
searchDrawerStateSelector,
state => state.selectedValue,
);
export const valueTypeDrawerSelector = createSelector(
selectedValueDrawerSelector,
state => state.valueType,
);
import { createSlice } from '@reduxjs/toolkit';
import { DrawerState } from '@/redux/drawer/drawer.types'; import { DrawerState } from '@/redux/drawer/drawer.types';
import { openDrawerReducer, closeDrawerReducer } from './drawer.reducers'; import { createSlice } from '@reduxjs/toolkit';
import {
clearSearchDrawerStateReducer,
closeDrawerReducer,
openDrawerReducer,
setSearchDrawerStateReducer,
} from './drawer.reducers';
const initialState: DrawerState = { const initialState: DrawerState = {
isOpen: false, isOpen: false,
drawerName: 'none', drawerName: 'none',
searchDrawerState: {
currentStep: 0,
selectedValue: {
name: '',
valueType: 'none',
},
},
}; };
const drawerSlice = createSlice({ const drawerSlice = createSlice({
...@@ -13,9 +25,12 @@ const drawerSlice = createSlice({ ...@@ -13,9 +25,12 @@ const drawerSlice = createSlice({
reducers: { reducers: {
openDrawer: openDrawerReducer, openDrawer: openDrawerReducer,
closeDrawer: closeDrawerReducer, closeDrawer: closeDrawerReducer,
setSearchDrawerState: setSearchDrawerStateReducer,
clearSearchDrawerState: clearSearchDrawerStateReducer,
}, },
}); });
export const { openDrawer, closeDrawer } = drawerSlice.actions; export const { openDrawer, closeDrawer, setSearchDrawerState, clearSearchDrawerState } =
drawerSlice.actions;
export default drawerSlice.reducer; export default drawerSlice.reducer;
import type { DrawerName } from '@/types/drawerName';
type DrugValue = {
name: string;
valueType: 'drugs';
};
type ChemicalsValue = {
name: string;
valueType: 'chemicals';
};
type MirnaValue = {
name: string;
valueType: 'mirna';
};
type BioEntityValue = {
model: { name: string; id: string };
name: string;
valueType: 'bioEntity';
};
type NoneValue = {
name: string;
valueType: 'none';
};
export type SelectedValue = DrugValue | ChemicalsValue | MirnaValue | BioEntityValue | NoneValue;
export type SearchDrawerState = {
currentStep: number;
selectedValue: SelectedValue;
};
export type DrawerState = { export type DrawerState = {
isOpen: boolean; isOpen: boolean;
drawerName: 'none' | 'search' | 'project-info' | 'plugins' | 'export' | 'legend'; drawerName: DrawerName;
searchDrawerState: SearchDrawerState;
}; };
import bioEntityContentsReducer from '@/redux/bioEntityContents/bioEntityContents.slice'; import bioEntityContentsReducer from '@/redux/bioEntityContents/bioEntityContents.slice';
import chemicalsReducer from '@/redux/chemicals/chemicals.slice'; import chemicalsReducer from '@/redux/chemicals/chemicals.slice';
import drawerReducer from '@/redux/drawer/drawer.slice'; import drawerReducer from '@/redux/drawer/drawer.slice';
import modelsReducer from '@/redux/models/models.slice';
import drugsReducer from '@/redux/drugs/drugs.slice'; import drugsReducer from '@/redux/drugs/drugs.slice';
import mirnasReducer from '@/redux/mirnas/mirnas.slice'; import mirnasReducer from '@/redux/mirnas/mirnas.slice';
import modelsReducer from '@/redux/models/models.slice';
import projectSlice from '@/redux/project/project.slice'; import projectSlice from '@/redux/project/project.slice';
import searchReducer from '@/redux/search/search.slice'; import searchReducer from '@/redux/search/search.slice';
import { configureStore } from '@reduxjs/toolkit'; import { configureStore } from '@reduxjs/toolkit';
......
...@@ -56,7 +56,11 @@ describe('DrawerHeadingBackwardButton - component', () => { ...@@ -56,7 +56,11 @@ describe('DrawerHeadingBackwardButton - component', () => {
it('should call class drawer on close button click', () => { it('should call class drawer on close button click', () => {
const { store } = renderComponent('Title', 'value', { const { store } = renderComponent('Title', 'value', {
drawer: { isOpen: true, drawerName: 'search' }, drawer: {
isOpen: true,
drawerName: 'search',
searchDrawerState: { currentStep: 0, selectedValue: { name: '', valueType: 'none' } },
},
}); });
expect(store.getState().drawer.isOpen).toBe(true); expect(store.getState().drawer.isOpen).toBe(true);
......
export type DrawerName = 'none' | 'search' | 'project-info' | 'plugins' | 'export' | 'legend';
export type PathName = 'none' | 'search' | 'project-info' | 'plugins' | 'export' | 'legend';
import { RootState, StoreType } from '@/redux/store';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import bioEntityContentsReducer from '@/redux/bioEntityContents/bioEntityContents.slice'; import bioEntityContentsReducer from '@/redux/bioEntityContents/bioEntityContents.slice';
import chemicalsReducer from '@/redux/chemicals/chemicals.slice'; import chemicalsReducer from '@/redux/chemicals/chemicals.slice';
import drawerReducer from '@/redux/drawer/drawer.slice'; import drawerReducer from '@/redux/drawer/drawer.slice';
import drugsReducer from '@/redux/drugs/drugs.slice'; import drugsReducer from '@/redux/drugs/drugs.slice';
import mirnasReducer from '@/redux/mirnas/mirnas.slice'; import mirnasReducer from '@/redux/mirnas/mirnas.slice';
import modelsReducer from '@/redux/models/models.slice';
import projectReducer from '@/redux/project/project.slice'; import projectReducer from '@/redux/project/project.slice';
import searchReducer from '@/redux/search/search.slice'; import searchReducer from '@/redux/search/search.slice';
import modelsReducer from '@/redux/models/models.slice'; import { RootState, StoreType } from '@/redux/store';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
interface WrapperProps { interface WrapperProps {
children: React.ReactNode; children: React.ReactNode;
......
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