Skip to content
Snippets Groups Projects
Commit 4323c1dc authored by Tadeusz Miesiąc's avatar Tadeusz Miesiąc
Browse files

feat(drugs results): display drugs search results

parent b95403b3
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...,!30Resolve MIN-96 "Feature/ open drawer after search"
Pipeline #79513 passed
Showing with 185 additions and 44 deletions
import { ToolkitStoreWithSingleSlice } from '@/utils/createStoreInstanceUsingSliceReducer';
import { DrawerState } from '@/redux/drawer/drawer.types';
import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
import { screen, render, act, fireEvent } from '@testing-library/react';
import drawerReducer, { 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 { Drawer } from './Drawer.component';
const renderComponent = (): { store: ToolkitStoreWithSingleSlice<DrawerState> } => {
const { Wrapper, store } = getReduxWrapperUsingSliceReducer('drawer', drawerReducer);
const renderComponent = (): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore();
return (
render(
<Wrapper>
......
import dynamic from 'next/dynamic';
import { twMerge } from 'tailwind-merge';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { drawerDataSelector } from '@/redux/drawer/drawer.selectors';
import { DRAWER_ROLE } from '@/components/Map/Drawer/Drawer.constants';
import { drawerSelector } from '@/redux/drawer/drawer.selectors';
const SearchDrawerContent = dynamic(
async () =>
......@@ -15,17 +15,17 @@ const SearchDrawerContent = dynamic(
);
export const Drawer = (): JSX.Element => {
const { open, drawerName } = useAppSelector(drawerDataSelector);
const { isOpen, drawerName } = useAppSelector(drawerSelector);
return (
<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',
isOpen && 'translate-x-0',
)}
role={DRAWER_ROLE}
>
{open && drawerName === 'search' && <SearchDrawerContent />}
{isOpen && drawerName === 'search' && <SearchDrawerContent />}
{/* other drawers comes here, should use dynamic import */}
</div>
);
......
import {
Accordion,
AccordionItem,
AccordionItemButton,
AccordionItemPanel,
......@@ -11,30 +10,28 @@ import { BioEntitiesSubmapItem } from '@/components/Map/Drawer/SearchDrawerConte
export const BioEntitiesAccordion = (): JSX.Element => {
const entity = { mapName: 'main map', numberOfEntities: 21 };
return (
<Accordion allowZeroExpanded>
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton>Content (2137)</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel className="">
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
</AccordionItemPanel>
</AccordionItem>
</Accordion>
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton>Content (2137)</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel className="">
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
<BioEntitiesSubmapItem
mapName={entity.mapName}
numberOfEntities={entity.numberOfEntities}
/>
</AccordionItemPanel>
</AccordionItem>
);
};
import { render, screen } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { drugsFixture } from '@/models/fixtures/drugFixtures';
import { Accordion } from '@/shared/Accordion';
import { DrugsAccordion } from './DrugsAccordion.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<Accordion>
<DrugsAccordion />
</Accordion>
</Wrapper>,
),
{
store,
}
);
};
describe('DrugsAccordion - component', () => {
it('should display drugs number after succesfull drug search', () => {
renderComponent({
drugs: { data: drugsFixture, loading: 'succeeded', error: { name: '', message: '' } },
});
expect(screen.getByText('Drugs (2)')).toBeInTheDocument();
});
it('should display loading indicator while waiting for drug search response', () => {
renderComponent({
drugs: { data: [], loading: 'pending', error: { name: '', message: '' } },
});
expect(screen.getByText('Drugs (Loading...)')).toBeInTheDocument();
});
});
import { loadingDrugsStatusSelector, numberOfDrugsSelector } from '@/redux/drugs/drugs.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { AccordionItem, AccordionItemHeading, AccordionItemButton } from '@/shared/Accordion';
export const DrugsAccordion = (): JSX.Element => {
const drugsNumber = useAppSelector(numberOfDrugsSelector);
const drugsState = useAppSelector(loadingDrugsStatusSelector);
return (
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton variant="non-expandable">
Drugs
{drugsState === 'pending' && ' (Loading...)'}
{drugsState === 'succeeded' && ` (${drugsNumber})`}
</AccordionItemButton>
</AccordionItemHeading>
</AccordionItem>
);
};
export { DrugsAccordion } from './DrugsAccordion.component';
import { BioEntitiesAccordion } from '@/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion';
import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerContent/DrugsAccordion';
import { closeDrawer } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { IconButton } from '@/shared/IconButton';
import { Accordion } from '@/shared/Accordion';
export const CLOSE_BUTTON_ROLE = 'close-drawer-button';
......@@ -28,7 +30,10 @@ export const SearchDrawerContent = (): JSX.Element => {
/>
</div>
<div className="px-6">
<BioEntitiesAccordion />
<Accordion allowZeroExpanded>
<BioEntitiesAccordion />
<DrugsAccordion />
</Accordion>
</div>
</div>
);
......
/* eslint-disable react/no-multi-comp */
import { Icon } from '@/shared/Icon';
import { AccordionItemButton as AIB } from 'react-accessible-accordion';
import './AccordionItemButton.style.css';
type Variant = 'expandable' | 'non-expandable';
interface AccordionItemButtonProps {
children: React.ReactNode;
variant?: Variant;
}
export const AccordionItemButton = ({ children }: AccordionItemButtonProps): JSX.Element => (
<AIB className="accordion-button flex flex-row flex-nowrap justify-between">
{children}
<Icon name="chevron-down" className="arrow-button h-6 w-6 fill-font-500" />
</AIB>
);
const getIcon = (variant: Variant): JSX.Element => {
const variantsIcons: Record<Variant, JSX.Element> = {
expandable: <Icon name="chevron-down" className="arrow-button h-6 w-6 fill-font-500" />,
'non-expandable': <Icon name="chevron-right" className="h-6 w-6 fill-font-500" />,
};
return variantsIcons[variant];
};
export const AccordionItemButton = ({
children,
variant = 'expandable',
}: AccordionItemButtonProps): JSX.Element => {
const ButtonIcon = getIcon(variant);
return (
<AIB className="accordion-button flex flex-row flex-nowrap justify-between">
{children}
{/* <Icon name="chevron-down" className="arrow-button h-6 w-6 fill-font-500" /> */}
{ButtonIcon}
</AIB>
);
};
import { store } from '@/redux/store';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import bioEntityContentsReducer from '@/redux/bioEntityContents/bioEntityContents.slice';
import chemicalsReducer from '@/redux/chemicals/chemicals.slice';
import drawerReducer from '@/redux/drawer/drawer.slice';
import drugsReducer from '@/redux/drugs/drugs.slice';
import mirnasReducer from '@/redux/mirnas/mirnas.slice';
import projectReducer from '@/redux/project/project.slice';
import searchReducer from '@/redux/search/search.slice';
import { SearchState } from '@/redux/search/search.types';
import { ProjectState } from '@/redux/project/project.types';
import { DrugsState } from '@/redux/drugs/drugs.types';
import { MirnasState } from '@/redux/mirnas/mirnas.types';
import { ChemicalsState } from '@/redux/chemicals/chemicals.types';
import { BioEntityContentsState } from '@/redux/bioEntityContents/bioEntityContents.types';
import { DrawerState } from '@/redux/drawer/drawer.types';
interface WrapperProps {
children: React.ReactNode;
}
export type InitialStoreState = {
search?: SearchState;
project?: ProjectState;
drugs?: DrugsState;
mirnas?: MirnasState;
chemicals?: ChemicalsState;
bioEntityContents?: BioEntityContentsState;
drawer?: DrawerState;
};
type GetReduxWrapperUsingSliceReducer = (initialState?: InitialStoreState) => {
Wrapper: ({ children }: WrapperProps) => JSX.Element;
store: typeof store;
};
export const getReduxWrapperWithStore: GetReduxWrapperUsingSliceReducer = (
preloadedState: InitialStoreState = {},
) => {
const testStore = configureStore({
reducer: {
search: searchReducer,
project: projectReducer,
drugs: drugsReducer,
mirnas: mirnasReducer,
chemicals: chemicalsReducer,
bioEntityContents: bioEntityContentsReducer,
drawer: drawerReducer,
},
preloadedState,
});
const Wrapper = ({ children }: WrapperProps): JSX.Element => (
<Provider store={testStore}>{children}</Provider>
);
return { Wrapper, store: testStore };
};
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