diff --git a/.eslintrc.json b/.eslintrc.json index f7df93bc3522f3a2dd376c7b93534d03b083103a..e60a21f06faa469391143c2f16c8d14f11ee5e97 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -71,7 +71,7 @@ "packageDir": "./" } ], - "indent": ["error", 2], + "indent": ["error", 2, { "SwitchCase": 1 }], "react/jsx-indent": ["error", 2], "react/jsx-indent-props": ["error", 2], "linebreak-style": ["error", "unix"], diff --git a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx index 66f5fc8ff7eb8b4982c37b3a4a0aed4213f28dd2..4cce8b0a86bcf5b80f073ca763b0e5ab2a0e2fb8 100644 --- a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx +++ b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx @@ -1,6 +1,6 @@ import lensIcon from '@/assets/vectors/icons/lens.svg'; import { isDrawerOpenSelector } from '@/redux/drawer/drawer.selectors'; -import { clearSearchDrawerState, openDrawer } from '@/redux/drawer/drawer.slice'; +import { openSearchDrawer } from '@/redux/drawer/drawer.slice'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { isPendingSearchStatusSelector, @@ -24,8 +24,7 @@ export const SearchBar = (): JSX.Element => { const openSearchDrawerIfClosed = (): void => { if (!isDrawerOpen) { - dispatch(openDrawer('search')); - dispatch(clearSearchDrawerState()); + dispatch(openSearchDrawer()); } }; diff --git a/src/components/Map/Drawer/Drawer.component.test.tsx b/src/components/Map/Drawer/Drawer.component.test.tsx index 1741384a561e63b950dfe8c63002fe99407033a0..26c7bd9828544a863a6198988441c7b9cdb7927b 100644 --- a/src/components/Map/Drawer/Drawer.component.test.tsx +++ b/src/components/Map/Drawer/Drawer.component.test.tsx @@ -1,4 +1,4 @@ -import { openDrawer } from '@/redux/drawer/drawer.slice'; +import { openSearchDrawer } from '@/redux/drawer/drawer.slice'; import { StoreType } from '@/redux/store'; import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; import { act, fireEvent, render, screen } from '@testing-library/react'; @@ -38,7 +38,7 @@ describe('Drawer - component', () => { expect(screen.queryByTestId('search-drawer-content')).not.toBeInTheDocument(); await act(() => { - store.dispatch(openDrawer('search')); + store.dispatch(openSearchDrawer()); }); expect(screen.getByTestId('search-drawer-content')).toBeInTheDocument(); @@ -48,7 +48,7 @@ describe('Drawer - component', () => { const { store } = renderComponent(); await act(() => { - store.dispatch(openDrawer('search')); + store.dispatch(openSearchDrawer()); }); expect(screen.getByTestId('search-drawer-content')).toBeInTheDocument(); diff --git a/src/components/Map/Drawer/Drawer.component.tsx b/src/components/Map/Drawer/Drawer.component.tsx index 52f4a1d53d3d1ebe9bcbc26259494e7b3253bbca..6415d40280dc158cb40cdde23b7a379432a76a0f 100644 --- a/src/components/Map/Drawer/Drawer.component.tsx +++ b/src/components/Map/Drawer/Drawer.component.tsx @@ -1,18 +1,8 @@ import { DRAWER_ROLE } from '@/components/Map/Drawer/Drawer.constants'; 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( - async () => - import('@/components/Map/Drawer/SearchDrawerContent').then( - module => module.SearchDrawerContent, - ), - { - ssr: false, - }, -); +import { SearchDrawerWrapper as SearchDrawerContent } from './SearchDrawerWrapper'; export const Drawer = (): JSX.Element => { const { isOpen, drawerName } = useAppSelector(drawerSelector); @@ -26,7 +16,6 @@ export const Drawer = (): JSX.Element => { role={DRAWER_ROLE} > {isOpen && drawerName === 'search' && <SearchDrawerContent />} - {/* other drawers comes here, should use dynamic import */} </div> ); }; diff --git a/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsList.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsList.component.tsx deleted file mode 100644 index 2678ebb1ba45b1e314797e321038f52d1b031ce5..0000000000000000000000000000000000000000 --- a/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsList.component.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { PinItem } from './PinsList.types'; -import { PinsListItem } from './PinsListItem'; - -interface PinsListProps { - pinsList: PinItem[]; -} - -export const PinsList = ({ pinsList }: PinsListProps): JSX.Element => ( - <ul className="px-6 py-2"> - {pinsList.map(pin => ( - <PinsListItem key={pin.name} name={pin.name} /> - ))} - </ul> -); diff --git a/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsList.types.tsx b/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsList.types.tsx deleted file mode 100644 index ce02ca44317ef35bcaa08a1f816c2b4157a4d91b..0000000000000000000000000000000000000000 --- a/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsList.types.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export type PinItem = { - name: string; -}; diff --git a/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsListItem/PinsListItem.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsListItem/PinsListItem.component.tsx deleted file mode 100644 index c6eba919fdc7c3dc7375bc62874cea9da02d8668..0000000000000000000000000000000000000000 --- a/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsListItem/PinsListItem.component.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { Icon } from '@/shared/Icon'; - -interface PinsListItemProps { - name: string; -} - -export const PinsListItem = ({ name }: PinsListItemProps): JSX.Element => ( - <div className="flex flex-row justify-between pt-4"> - <Icon name="pin" className="mr-2 shrink-0" /> - <p className="w-full text-left">{name}</p> - <Icon name="chevron-right" className="h-6 w-6 shrink-0" /> - </div> -); diff --git a/src/components/Map/Drawer/SearchDrawerContent/Results/Results.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/Results/Results.component.tsx deleted file mode 100644 index 89b9cd601d70e334669de335f5abb0acf793976a..0000000000000000000000000000000000000000 --- a/src/components/Map/Drawer/SearchDrawerContent/Results/Results.component.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton'; -import { PinsList } from './PinsList'; -import { PinItem } from './PinsList/PinsList.types'; - -const PINS_LIST: PinItem[] = [ - { name: 'Glyceraldehyde-3-phosphate dehydrogenase' }, - { name: 'D-3-phosphoglycerate dehydrogenase' }, - { name: 'Glutathione reductase, mitochondrial' }, - { name: 'NADH dehydrogenase [ubiquinone] iron-sulfur protein 8, mitochondrial' }, -]; - -export const Results = (): JSX.Element => { - const drugName = 'Aspirin'; - - const navigateToGroupedSearchResults = (): void => {}; - - return ( - <div> - <DrawerHeadingBackwardButton - title="Drugs" - value={drugName} - backwardFunction={navigateToGroupedSearchResults} - /> - <PinsList pinsList={PINS_LIST} /> - </div> - ); -}; diff --git a/src/components/Map/Drawer/SearchDrawerContent/Results/index.ts b/src/components/Map/Drawer/SearchDrawerContent/Results/index.ts deleted file mode 100644 index 0b2da8f8d99a9b80340d6ba81a92990adb10029d..0000000000000000000000000000000000000000 --- a/src/components/Map/Drawer/SearchDrawerContent/Results/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Results } from './Results.component'; diff --git a/src/components/Map/Drawer/SearchDrawerContent/index.ts b/src/components/Map/Drawer/SearchDrawerContent/index.ts deleted file mode 100644 index 72074d52d0f624c1d2bb20a782da720e40692531..0000000000000000000000000000000000000000 --- a/src/components/Map/Drawer/SearchDrawerContent/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SearchDrawerContent } from './SearchDrawerContent.component'; diff --git a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx similarity index 92% rename from src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx index 6442eca6bb1de817ab1ee0dfdf8c343b5c24edea..1fecf9a78fc3001ccafb6b6fd2f6c6921df64f61 100644 --- a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx @@ -5,7 +5,7 @@ import { AccordionItemHeading, } from '@/shared/Accordion'; -import { BioEntitiesSubmapItem } from '@/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesSubmapItem'; +import { BioEntitiesSubmapItem } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem'; export const BioEntitiesAccordion = (): JSX.Element => { const entity = { mapName: 'main map', numberOfEntities: 21 }; diff --git a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx diff --git a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.tsx similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.tsx diff --git a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesSubmapItem/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/index.ts similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesSubmapItem/index.ts rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesSubmapItem/index.ts diff --git a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/index.ts similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/index.ts rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/index.ts diff --git a/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx diff --git a/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.tsx similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.tsx diff --git a/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/index.ts similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/index.ts rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/index.ts diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.test.tsx similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.test.tsx diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.tsx similarity index 57% rename from src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.tsx index af14387558dbfecd9b62eef326603c17dd49743c..4053e7f637bfb49300b337b660f535cd3187bfb1 100644 --- a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.tsx @@ -1,15 +1,29 @@ +import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; +import { displayDrugsList } from '@/redux/drawer/drawer.slice'; import { loadingDrugsStatusSelector, numberOfDrugsSelector } from '@/redux/drugs/drugs.selectors'; +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; 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); + const dispatch = useAppDispatch(); + + const isPending = drugsState === 'pending'; + + const onAccordionClick = (): void => { + dispatch(displayDrugsList()); + }; return ( <AccordionItem> <AccordionItemHeading> - <AccordionItemButton variant="non-expandable"> + <AccordionItemButton + variant="non-expandable" + onClick={onAccordionClick} + disabled={isPending || drugsNumber === SIZE_OF_EMPTY_ARRAY} + > Drugs {drugsState === 'pending' && ' (Loading...)'} {drugsState === 'succeeded' && ` (${drugsNumber})`} diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/index.ts similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/index.ts diff --git a/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/GroupedSearchResults.component.tsx similarity index 79% rename from src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/GroupedSearchResults.component.tsx index 7f0cc18beaeafb98364d31dedab045ab00c4d7d1..29a6076e9937a13aea95d1245c0e8c3c2ac48834 100644 --- a/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/GroupedSearchResults.component.tsx @@ -1,7 +1,7 @@ -import { BioEntitiesAccordion } from '@/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion'; -import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerContent/DrugsAccordion'; -import { ChemicalsAccordion } from '@/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion'; -import { MirnaAccordion } from '@/components/Map/Drawer/SearchDrawerContent/MirnaAccordion'; +import { BioEntitiesAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion'; +import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion'; +import { ChemicalsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion'; +import { MirnaAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion'; import { closeDrawer } from '@/redux/drawer/drawer.slice'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { IconButton } from '@/shared/IconButton'; @@ -9,7 +9,7 @@ import { Accordion } from '@/shared/Accordion'; export const CLOSE_BUTTON_ROLE = 'close-drawer-button'; -export const SearchDrawerContent = (): JSX.Element => { +export const GroupedSearchResults = (): JSX.Element => { const dispatch = useAppDispatch(); const handleCloseDrawer = (): void => { @@ -17,7 +17,7 @@ export const SearchDrawerContent = (): JSX.Element => { }; return ( - <div className="flex flex-col" data-testid="search-drawer-content"> + <div className="flex flex-col" data-testid="grouped-search-results"> <div className="flex items-center justify-between border-b border-b-divide px-6"> <div className=" py-8 text-xl"> <span className="font-normal">Search: </span> diff --git a/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.test.tsx similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.test.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.test.tsx diff --git a/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.tsx similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.tsx rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.tsx diff --git a/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/index.ts similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/index.ts rename to src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/index.ts diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..99f53f8e7845637008091edf91ce2a73464e8dc1 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/index.ts @@ -0,0 +1 @@ +export { GroupedSearchResults } from './GroupedSearchResults.component'; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..676b58b498fe22ae75ade2b35bc7b5655b6b4bd3 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.test.tsx @@ -0,0 +1,60 @@ +/* eslint-disable no-magic-numbers */ +import { render, screen } from '@testing-library/react'; +import { + InitialStoreState, + getReduxWrapperWithStore, +} from '@/utils/testing/getReduxWrapperWithStore'; +import { StoreType } from '@/redux/store'; +import { drugsFixture } from '@/models/fixtures/drugFixtures'; +import { drawerSearchDrugsStepTwoFixture } from '@/redux/drawer/drawerFixture'; +import { PinsList } from './PinsList.component'; + +const PINS_LIST = drugsFixture.map(drug => ({ + id: drug.id, + name: drug.name, + data: drug, +})); + +const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { + const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); + + return ( + render( + <Wrapper> + <PinsList pinsList={PINS_LIST} type="drugs" /> + </Wrapper>, + ), + { + store, + } + ); +}; + +describe('PinsList component', () => { + it('should render list of pins', () => { + renderComponent(); + + const fristDrugName = drugsFixture[0].name; + const secondDrugName = drugsFixture[1].name; + + expect(screen.getByText(fristDrugName)).toBeInTheDocument(); + expect(screen.getByText(secondDrugName)).toBeInTheDocument(); + }); + it('should navigate to details step on pin click', () => { + const { store } = renderComponent({ drawer: drawerSearchDrugsStepTwoFixture }); + + const firstPin = screen.getAllByRole('button')[0]; + firstPin.click(); + + const { + drawer: { + searchDrawerState: { currentStep, stepType, selectedValue }, + }, + } = store.getState(); + const drug = drugsFixture[0]; + + expect(currentStep).toBe(3); + expect(stepType).toBe('drugs'); + expect(selectedValue).toEqual(drug); + }); +}); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..caa8ea04e9a0ee89c4aea8dfe5e041ad00236366 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx @@ -0,0 +1,31 @@ +import { BioEntityContent, Chemical, Drug, Mirna } from '@/types/models'; +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; +import { displayEntityDetails } from '@/redux/drawer/drawer.slice'; +import { PinItem, PinType } from './PinsList.types'; +import { PinsListItem } from './PinsListItem'; + +interface PinsListProps { + pinsList: PinItem[]; + type: PinType; +} + +export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => { + const dispatch = useAppDispatch(); + + const onPinClick = (data: BioEntityContent | Drug | Chemical | Mirna): void => { + dispatch(displayEntityDetails(data)); + }; + + return ( + <ul className="px-6 py-2"> + {pinsList.map(pin => ( + <PinsListItem + key={pin.id} + name={pin.name} + type={type} + onClick={(): void => onPinClick(pin.data)} + /> + ))} + </ul> + ); +}; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.types.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.types.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5369b84d3ce8b27b358e24251e62cbdb79d544d8 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.types.tsx @@ -0,0 +1,9 @@ +import { BioEntityContent, Drug, Chemical, Mirna } from '@/types/models'; + +export type PinItem = { + id: string | number; + name: string; + data: Drug | Chemical | Mirna | BioEntityContent; +}; + +export type PinType = 'chemicals' | 'drugs' | 'mirna' | 'bioEntity' | 'none'; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6b8e36b7d37371728aad043eefce11703837d6c0 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.tsx @@ -0,0 +1,18 @@ +import { twMerge } from 'tailwind-merge'; +import { Icon } from '@/shared/Icon'; +import { getPinColor } from './PinsListItem.component.utils'; +import { PinType } from '../PinsList.types'; + +interface PinsListItemProps { + name: string; + type: PinType; + onClick: () => void; +} + +export const PinsListItem = ({ name, type, onClick }: PinsListItemProps): JSX.Element => ( + <button className="flex flex-row justify-between pt-4" onClick={onClick} type="button"> + <Icon name="pin" className={twMerge('mr-2 shrink-0', getPinColor(type))} /> + <p className="w-full text-left">{name}</p> + <Icon name="chevron-right" className="h-6 w-6 shrink-0" /> + </button> +); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.utils.ts b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..873de967bd4fcd13d7b8c66a631856a587507f6b --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.utils.ts @@ -0,0 +1,19 @@ +import { assertNever } from '@/utils/assertNever'; +import { PinType } from '../PinsList.types'; + +export const getPinColor = (type: PinType): string => { + switch (type) { + case 'bioEntity': + return 'fill-primary-500'; + case 'drugs': + return 'fill-orange'; + case 'chemicals': + return 'fill-purple'; + case 'mirna': + return 'fill-primary-500'; + case 'none': + return ''; + default: + return assertNever(type); + } +}; diff --git a/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsListItem/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/index.ts similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsListItem/index.ts rename to src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/index.ts diff --git a/src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/index.ts similarity index 100% rename from src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/index.ts rename to src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/index.ts diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.test.tsx new file mode 100644 index 0000000000000000000000000000000000000000..7e310b6ba19f3df32f889526f1f555c303f36808 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.test.tsx @@ -0,0 +1,86 @@ +/* eslint-disable no-magic-numbers */ +import { act, render, screen } from '@testing-library/react'; +import { + InitialStoreState, + getReduxWrapperWithStore, +} from '@/utils/testing/getReduxWrapperWithStore'; +import { StoreType } from '@/redux/store'; +import { drugsFixture } from '@/models/fixtures/drugFixtures'; +import { ResultsList } from './ResultsList.component'; + +const INITIAL_STATE: InitialStoreState = { + search: { + searchValue: 'aspirin', + loading: 'idle', + }, + drawer: { + isOpen: true, + drawerName: 'search', + searchDrawerState: { + currentStep: 2, + stepType: 'drugs', + selectedValue: undefined, + }, + }, + drugs: { + data: drugsFixture, + loading: 'succeeded', + error: { name: '', message: '' }, + }, +}; + +const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { + const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); + + return ( + render( + <Wrapper> + <ResultsList /> + </Wrapper>, + ), + { + store, + } + ); +}; + +describe('ResultsList - component ', () => { + it('should render results and navigation panel', () => { + renderComponent(INITIAL_STATE); + + expect(screen.getByText('drugs:')).toBeInTheDocument(); + expect(screen.getByText('aspirin')).toBeInTheDocument(); + + const fristDrugName = drugsFixture[0].name; + const secondDrugName = drugsFixture[1].name; + + expect(screen.getByText(fristDrugName)).toBeInTheDocument(); + expect(screen.getByText(secondDrugName)).toBeInTheDocument(); + }); + it('should navigate to grouped search results after backward button click', async () => { + const { store } = renderComponent(INITIAL_STATE); + + const { + drawer: { + searchDrawerState: { currentStep, stepType }, + }, + } = store.getState(); + + expect(currentStep).toBe(2); + expect(stepType).toBe('drugs'); + + const backwardButton = screen.getByRole('close-drawer-button'); + await act(() => { + backwardButton.click(); + }); + + const { + drawer: { + searchDrawerState: { currentStep: updatedStep, stepType: updatedStepType }, + }, + } = store.getState(); + + expect(updatedStep).toBe(1); + expect(updatedStepType).toBe('none'); + }); +}); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..25000b8f48fd60afb648915f6a8302a9bfa3341e --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.tsx @@ -0,0 +1,29 @@ +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { resultListSelector, stepTypeDrawerSelector } from '@/redux/drawer/drawer.selectors'; +import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton'; +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; +import { displayGroupedSearchResults } from '@/redux/drawer/drawer.slice'; +import { searchValueSelector } from '@/redux/search/search.selectors'; +import { PinsList } from './PinsList'; + +export const ResultsList = (): JSX.Element => { + const dispatch = useAppDispatch(); + const data = useAppSelector(resultListSelector); + const stepType = useAppSelector(stepTypeDrawerSelector); + const searchValue = useAppSelector(searchValueSelector); + + const navigateToGroupedSearchResults = (): void => { + dispatch(displayGroupedSearchResults()); + }; + + return ( + <div> + <DrawerHeadingBackwardButton + title={stepType} + value={searchValue} + backwardFunction={navigateToGroupedSearchResults} + /> + {data && <PinsList pinsList={data} type={stepType} />} + </div> + ); +}; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/index.ts b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5ca1e342f9ed184a55d957f019d6cdb36488117b --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/index.ts @@ -0,0 +1 @@ +export { ResultsList } from './ResultsList.component'; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.test.tsx index 19c588d71caddd0b92fbadf301ff589931ef559c..6322951135c2f2c34a38c96226e93783a3af004b 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.test.tsx @@ -1,4 +1,7 @@ import { SearchDrawerWrapper } from '@/components/Map/Drawer/SearchDrawerWrapper'; +import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture'; +import { drugFixture } from '@/models/fixtures/drugFixtures'; +import { drawerSearchStepOneFixture } from '@/redux/drawer/drawerFixture'; import { StoreType } from '@/redux/store'; import { InitialStoreState, @@ -24,20 +27,10 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St describe('SearchDrawerWrapper - component', () => { it('should display the first step for search', () => { renderComponent({ - drawer: { - isOpen: true, - drawerName: 'search', - searchDrawerState: { - currentStep: 1, - selectedValue: { - name: '', - valueType: 'none', - }, - }, - }, + drawer: drawerSearchStepOneFixture, }); - expect(screen.getByTestId('search-first-step')).toBeInTheDocument(); + expect(screen.getByTestId('grouped-search-results')).toBeInTheDocument(); }); it('should display the second step for value type bioEntity', () => { @@ -47,11 +40,8 @@ describe('SearchDrawerWrapper - component', () => { drawerName: 'search', searchDrawerState: { currentStep: 2, - selectedValue: { - model: { name: 'test model bioEntity', id: 'test-id' }, - name: 'bio entity second step', - valueType: 'bioEntity', - }, + stepType: 'bioEntity', + selectedValue: undefined, }, }, }); @@ -59,17 +49,15 @@ describe('SearchDrawerWrapper - component', () => { expect(screen.getByTestId('search-second-step')).toBeInTheDocument(); }); - it('should display the second step for value type chemicals', () => { + it('should display the second step for value type drugs', () => { renderComponent({ drawer: { isOpen: true, drawerName: 'search', searchDrawerState: { currentStep: 2, - selectedValue: { - name: 'chemicals second step', - valueType: 'chemicals', - }, + stepType: 'drugs', + selectedValue: undefined, }, }, }); @@ -84,11 +72,8 @@ describe('SearchDrawerWrapper - component', () => { drawerName: 'search', searchDrawerState: { currentStep: 3, - selectedValue: { - model: { name: 'test model bioEntity', id: 'test-id' }, - name: 'bio entity third step', - valueType: 'bioEntity', - }, + stepType: 'bioEntity', + selectedValue: bioEntityContentFixture, }, }, }); @@ -96,17 +81,15 @@ describe('SearchDrawerWrapper - component', () => { expect(screen.getByTestId('search-third-step')).toBeInTheDocument(); }); - it('should display the third step for value type chemicals', () => { + it('should display the third step for value type drugs', () => { renderComponent({ drawer: { isOpen: true, drawerName: 'search', searchDrawerState: { currentStep: 3, - selectedValue: { - name: 'chemicals third step', - valueType: 'chemicals', - }, + stepType: 'drugs', + selectedValue: drugFixture, }, }, }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.tsx index 9ce500cf316b75bbc4616efe6afe1246d58b87e6..23aa97631a454e639c903932d0b297ad644b60ce 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.tsx @@ -1,29 +1,33 @@ -import { STEP } from '@/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.constants'; import { BIO_ENTITY, DRUGS_CHEMICALS_MIRNA } from '@/constants'; +import { STEP } from '@/constants/searchDrawer'; import { currentStepDrawerStateSelector, - valueTypeDrawerSelector, + stepTypeDrawerSelector, } from '@/redux/drawer/drawer.selectors'; import { useSelector } from 'react-redux'; +import { ResultsList } from '@/components/Map/Drawer/SearchDrawerWrapper/ResultsList'; +import { GroupedSearchResults } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults'; export const SearchDrawerWrapper = (): JSX.Element => { const currentStep = useSelector(currentStepDrawerStateSelector); - const valueType = useSelector(valueTypeDrawerSelector); + const stepType = useSelector(stepTypeDrawerSelector); - const isBioEntityType = valueType === BIO_ENTITY; - const isChemicalsDrugsOrMirnaType = DRUGS_CHEMICALS_MIRNA.includes(valueType); + const isBioEntityType = stepType === BIO_ENTITY; + const isChemicalsDrugsOrMirnaType = DRUGS_CHEMICALS_MIRNA.includes(stepType); return ( - <div> + <div data-testid="search-drawer-content"> {/* first step for displaying search results, drawers etc */} - {currentStep === STEP.FIRST && <div data-testid="search-first-step">The first step</div>} + {currentStep === STEP.FIRST && <GroupedSearchResults />} {/* 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> + <div data-testid="search-second-step"> + <ResultsList /> + </div> )} {/* last step for bioentity */} {currentStep === STEP.THIRD && isBioEntityType && ( diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.constants.ts b/src/constants/searchDrawer.ts similarity index 100% rename from src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.constants.ts rename to src/constants/searchDrawer.ts diff --git a/src/models/fixtures/bioEntityContentsFixture.ts b/src/models/fixtures/bioEntityContentsFixture.ts index adc441d36ea3283a78f4e9134b1be4cbc8f0455d..fec6a7137e8f820642686b4104f4450e15ac5b6d 100644 --- a/src/models/fixtures/bioEntityContentsFixture.ts +++ b/src/models/fixtures/bioEntityContentsFixture.ts @@ -8,3 +8,8 @@ export const bioEntityContentsFixture = createFixture(z.array(bioEntityContentSc seed: ZOD_SEED, array: { min: 2, max: 2 }, }); + +export const bioEntityContentFixture = createFixture(bioEntityContentSchema, { + seed: ZOD_SEED, + array: { min: 1, max: 1 }, +}); diff --git a/src/models/fixtures/drugFixtures.ts b/src/models/fixtures/drugFixtures.ts index 44c60b79eabff4c457ecd14d1f1c5ec0bb398cf5..b7d74539f4873cfe2008de418001d973a7142cd8 100644 --- a/src/models/fixtures/drugFixtures.ts +++ b/src/models/fixtures/drugFixtures.ts @@ -8,3 +8,8 @@ export const drugsFixture = createFixture(z.array(drugSchema), { seed: ZOD_SEED, array: { min: 2, max: 2 }, }); + +export const drugFixture = createFixture(drugSchema, { + seed: ZOD_SEED, + array: { min: 1, max: 1 }, +}); diff --git a/src/redux/drawer/drawer.reducers.test.ts b/src/redux/drawer/drawer.reducers.test.ts index d7bf3cedfe4bccb7fb41301f747220119f204573..58f37fa1eeaf89d1a9d41e03ad6640e5a787675e 100644 --- a/src/redux/drawer/drawer.reducers.test.ts +++ b/src/redux/drawer/drawer.reducers.test.ts @@ -1,11 +1,14 @@ import * as toolkitRaw from '@reduxjs/toolkit'; import { AnyAction } from '@reduxjs/toolkit'; import type { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore'; +import { drugFixture } from '@/models/fixtures/drugFixtures'; import drawerReducer, { - clearSearchDrawerState, closeDrawer, + displayDrugsList, + displayEntityDetails, + displayGroupedSearchResults, openDrawer, - setSearchDrawerState, + openSearchDrawer, } from './drawer.slice'; import type { DrawerState } from './drawer.types'; @@ -14,13 +17,17 @@ const INITIAL_STATE: DrawerState = { drawerName: 'none', searchDrawerState: { currentStep: 0, - selectedValue: { - name: '', - valueType: 'none', - }, + stepType: 'none', + selectedValue: undefined, }, }; +const STEP = { + FIRST: 1, + SECOND: 2, + THIRD: 3, +}; + type SliceReducerType = ToolkitStore< { drawer: DrawerState; @@ -48,7 +55,7 @@ describe('drawer reducer', () => { expect(drawerReducer(undefined, action)).toEqual(INITIAL_STATE); }); - it('should update the store when you click a project info button on the nav bar', async () => { + it('should update the store after openDrawer action', async () => { const { type } = await store.dispatch(openDrawer('project-info')); const { isOpen, drawerName } = store.getState().drawer; @@ -57,60 +64,68 @@ describe('drawer reducer', () => { expect(drawerName).toEqual('project-info'); }); - it('should update the store when you click the close button on the drawer', async () => { + it('should update the store after openSearchDrawer action', async () => { + const { type } = await store.dispatch(openSearchDrawer()); + const { + isOpen, + drawerName, + searchDrawerState: { currentStep }, + } = store.getState().drawer; + + expect(type).toBe('drawer/openSearchDrawer'); + expect(isOpen).toBe(true); + expect(drawerName).toEqual('search'); + expect(currentStep).toEqual(STEP.FIRST); + }); + + it('should update the store after closeDrawerReducer action', async () => { const { type } = await store.dispatch(closeDrawer()); - const { isOpen, drawerName } = store.getState().drawer; + const { + isOpen, + drawerName, + searchDrawerState: { currentStep, selectedValue, stepType }, + } = store.getState().drawer; - expect(type).toBe('drawer/closeDrawer'); expect(isOpen).toBe(false); - expect(drawerName).toEqual('none'); + expect(drawerName).toBe('none'); + expect(currentStep).toBe(STEP.FIRST); + expect(selectedValue).toEqual(undefined); + expect(stepType).toEqual('none'); + expect(type).toBe('drawer/closeDrawer'); }); - it('should update the store when you choose from the drawer chemical`s first step', async () => { - 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 after displayDrugsList action', async () => { + const { type } = await store.dispatch(displayDrugsList()); + const { + drawerName, + searchDrawerState: { currentStep, stepType }, + } = store.getState().drawer; + + expect(type).toBe('drawer/displayDrugsList'); + expect(drawerName).toBe('search'); + expect(currentStep).toBe(STEP.SECOND); + expect(stepType).toEqual('drugs'); }); - 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', - }); + it('should update the store after displayGroupedSearchResults action', async () => { + const { type } = await store.dispatch(displayGroupedSearchResults()); + const { + searchDrawerState: { currentStep, stepType }, + } = store.getState().drawer; + + expect(type).toBe('drawer/displayGroupedSearchResults'); + expect(currentStep).toBe(STEP.FIRST); + expect(stepType).toEqual('none'); + }); + + it('should update the store after displayEntityDetails action', async () => { + const { type } = await store.dispatch(displayEntityDetails(drugFixture)); + const { + searchDrawerState: { currentStep, selectedValue }, + } = store.getState().drawer; + + expect(type).toBe('drawer/displayEntityDetails'); + expect(currentStep).toBe(STEP.THIRD); + expect(selectedValue).toEqual(drugFixture); }); }); diff --git a/src/redux/drawer/drawer.reducers.ts b/src/redux/drawer/drawer.reducers.ts index e205e9a44d2a73955b70020f07b827ac86333096..84714e5f3660ed6edaffcf4399d5600ab96b5cc3 100644 --- a/src/redux/drawer/drawer.reducers.ts +++ b/src/redux/drawer/drawer.reducers.ts @@ -1,3 +1,4 @@ +import { STEP } from '@/constants/searchDrawer'; import type { DrawerState } from '@/redux/drawer/drawer.types'; import type { DrawerName } from '@/types/drawerName'; import type { PayloadAction } from '@reduxjs/toolkit'; @@ -7,24 +8,35 @@ export const openDrawerReducer = (state: DrawerState, action: PayloadAction<Draw state.drawerName = action.payload; }; +export const openSearchDrawerReducer = (state: DrawerState): void => { + state.isOpen = true; + state.drawerName = 'search'; + state.searchDrawerState.currentStep = STEP.FIRST; +}; + export const closeDrawerReducer = (state: DrawerState): void => { state.isOpen = false; + state.drawerName = 'none'; + state.searchDrawerState.currentStep = STEP.FIRST; + state.searchDrawerState.selectedValue = undefined; + state.searchDrawerState.stepType = 'none'; }; -export const setSearchDrawerStateReducer = ( - state: DrawerState, - action: PayloadAction<DrawerState['searchDrawerState']>, -): void => { - const { currentStep, selectedValue } = action.payload; +export const displayDrugsListReducer = (state: DrawerState): void => { + state.drawerName = 'search'; + state.searchDrawerState.currentStep = STEP.SECOND; + state.searchDrawerState.stepType = 'drugs'; +}; - state.searchDrawerState.currentStep = currentStep; - state.searchDrawerState.selectedValue = selectedValue; +export const displayGroupedSearchResultsReducer = (state: DrawerState): void => { + state.searchDrawerState.currentStep = STEP.FIRST; + state.searchDrawerState.stepType = 'none'; }; -export const clearSearchDrawerStateReducer = (state: DrawerState): void => { - state.searchDrawerState.currentStep = 0; - state.searchDrawerState.selectedValue = { - name: '', - valueType: 'none', - }; +export const displayEntityDetailsReducer = ( + state: DrawerState, + action: PayloadAction<DrawerState['searchDrawerState']['selectedValue']>, +): void => { + state.searchDrawerState.currentStep = STEP.THIRD; + state.searchDrawerState.selectedValue = action.payload; }; diff --git a/src/redux/drawer/drawer.selectors.ts b/src/redux/drawer/drawer.selectors.ts index a622a1f6146c2ffbf97b32803f618661b1b2fc9b..b928a6bddf639538d06f6992b3d3968778d5ddbe 100644 --- a/src/redux/drawer/drawer.selectors.ts +++ b/src/redux/drawer/drawer.selectors.ts @@ -1,4 +1,5 @@ import { rootSelector } from '@/redux/root/root.selectors'; +import { assertNever } from '@/utils/assertNever'; import { createSelector } from '@reduxjs/toolkit'; export const drawerSelector = createSelector(rootSelector, state => state.drawer); @@ -20,7 +21,34 @@ export const selectedValueDrawerSelector = createSelector( state => state.selectedValue, ); -export const valueTypeDrawerSelector = createSelector( - selectedValueDrawerSelector, - state => state.valueType, +export const stepTypeDrawerSelector = createSelector( + searchDrawerStateSelector, + state => state.stepType, ); + +export const resultListSelector = createSelector(rootSelector, state => { + const selectedType = state.drawer.searchDrawerState.stepType; + + switch (selectedType) { + case 'drugs': + return state.drugs.data!.map(drug => ({ + id: drug.id, + name: drug.name, + data: drug, + })); + case 'chemicals': + return state.chemicals.data!.map(chemical => ({ + id: chemical.id.id, + name: chemical.name, + data: chemical, + })); + case 'bioEntity': + return undefined; + case 'mirna': + return state.mirnas.data!.map(mirna => ({ id: mirna.id, name: mirna.name, data: mirna })); + case 'none': + return undefined; + default: + return assertNever(selectedType); + } +}); diff --git a/src/redux/drawer/drawer.slice.ts b/src/redux/drawer/drawer.slice.ts index cbbe5a331f263507dd35c55d639846b9dd208714..153ee4cca689e3c9660b155229736eadf8e16fa6 100644 --- a/src/redux/drawer/drawer.slice.ts +++ b/src/redux/drawer/drawer.slice.ts @@ -1,10 +1,12 @@ import { DrawerState } from '@/redux/drawer/drawer.types'; import { createSlice } from '@reduxjs/toolkit'; import { - clearSearchDrawerStateReducer, closeDrawerReducer, + displayDrugsListReducer, + displayEntityDetailsReducer, + displayGroupedSearchResultsReducer, openDrawerReducer, - setSearchDrawerStateReducer, + openSearchDrawerReducer, } from './drawer.reducers'; const initialState: DrawerState = { @@ -12,10 +14,8 @@ const initialState: DrawerState = { drawerName: 'none', searchDrawerState: { currentStep: 0, - selectedValue: { - name: '', - valueType: 'none', - }, + stepType: 'none', + selectedValue: undefined, }, }; @@ -24,13 +24,21 @@ const drawerSlice = createSlice({ initialState, reducers: { openDrawer: openDrawerReducer, + openSearchDrawer: openSearchDrawerReducer, closeDrawer: closeDrawerReducer, - setSearchDrawerState: setSearchDrawerStateReducer, - clearSearchDrawerState: clearSearchDrawerStateReducer, + displayDrugsList: displayDrugsListReducer, + displayGroupedSearchResults: displayGroupedSearchResultsReducer, + displayEntityDetails: displayEntityDetailsReducer, }, }); -export const { openDrawer, closeDrawer, setSearchDrawerState, clearSearchDrawerState } = - drawerSlice.actions; +export const { + openDrawer, + openSearchDrawer, + closeDrawer, + displayDrugsList, + displayGroupedSearchResults, + displayEntityDetails, +} = drawerSlice.actions; export default drawerSlice.reducer; diff --git a/src/redux/drawer/drawer.types.ts b/src/redux/drawer/drawer.types.ts index 404d0fc2c1c6163b21fc69e6269b2ee14d49a503..e39ff163a7d9ac9eb764d1228e4cc58e50e4e422 100644 --- a/src/redux/drawer/drawer.types.ts +++ b/src/redux/drawer/drawer.types.ts @@ -1,36 +1,10 @@ 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; +import { BioEntityContent, Chemical, Drug, Mirna } from '@/types/models'; export type SearchDrawerState = { currentStep: number; - selectedValue: SelectedValue; + stepType: 'bioEntity' | 'drugs' | 'mirna' | 'chemicals' | 'none'; + selectedValue: BioEntityContent | Drug | Mirna | Chemical | undefined; }; export type DrawerState = { diff --git a/src/redux/drawer/drawerFixture.ts b/src/redux/drawer/drawerFixture.ts new file mode 100644 index 0000000000000000000000000000000000000000..46c1896ac9808ffe7a802cc9db9c746ce95c1529 --- /dev/null +++ b/src/redux/drawer/drawerFixture.ts @@ -0,0 +1,31 @@ +import { DrawerState } from './drawer.types'; + +export const initialStateFixture: DrawerState = { + isOpen: false, + drawerName: 'none', + searchDrawerState: { + currentStep: 0, + stepType: 'none', + selectedValue: undefined, + }, +}; + +export const drawerSearchStepOneFixture: DrawerState = { + isOpen: true, + drawerName: 'search', + searchDrawerState: { + currentStep: 1, + stepType: 'none', + selectedValue: undefined, + }, +}; + +export const drawerSearchDrugsStepTwoFixture: DrawerState = { + isOpen: true, + drawerName: 'search', + searchDrawerState: { + currentStep: 2, + stepType: 'drugs', + selectedValue: undefined, + }, +}; diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx index 09603e0609e575f13e43dd5d4facdd18eb6763cf..d4a56484635760d1a5de258f993969a1878d0655 100644 --- a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx +++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx @@ -3,21 +3,32 @@ import './AccordionItemButton.style.css'; import { Variant } from './AccordionItemButton.types'; import { getIcon } from './AccordionItemButton.utils'; -interface AccordionItemButtonProps { +type AccordionItemButtonProps = { children: React.ReactNode; variant?: Variant; -} + onClick?: () => void; + disabled?: boolean; +}; export const AccordionItemButton = ({ children, variant = 'expandable', + onClick, + disabled, }: AccordionItemButtonProps): JSX.Element => { const ButtonIcon = getIcon(variant); return ( <AIB className="accordion-button flex flex-row flex-nowrap justify-between"> - {children} - {ButtonIcon} + <button + onClick={onClick} + disabled={disabled} + className="flex w-full flex-row flex-nowrap justify-between text-sm" + type="button" + > + {children} + {ButtonIcon} + </button> </AIB> ); }; diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.style.css b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.style.css index ee300f20ba887d33a867a0524f9683a95d85d4cf..9da947bbf0e48c0a5c994b6b9bcd542923d29c5f 100644 --- a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.style.css +++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.style.css @@ -1,3 +1,3 @@ -.accordion-button[aria-expanded='true'] > .arrow-button { +.accordion-button[aria-expanded='true'] .arrow-button { @apply rotate-180; } diff --git a/src/shared/DrawerHeadingBackwardButton/DrawerHeadingBackwardButton.component.test.tsx b/src/shared/DrawerHeadingBackwardButton/DrawerHeadingBackwardButton.component.test.tsx index e35e20491cc5d1f1b2e16ed4bd12b0866ad41df0..38cee7f2d5a8ab8f299890452130e9e86522f6fc 100644 --- a/src/shared/DrawerHeadingBackwardButton/DrawerHeadingBackwardButton.component.test.tsx +++ b/src/shared/DrawerHeadingBackwardButton/DrawerHeadingBackwardButton.component.test.tsx @@ -4,6 +4,7 @@ import { getReduxWrapperWithStore, } from '@/utils/testing/getReduxWrapperWithStore'; import { render, screen } from '@testing-library/react'; +import { drawerSearchStepOneFixture } from '@/redux/drawer/drawerFixture'; import { DrawerHeadingBackwardButton } from './DrawerHeadingBackwardButton.component'; const backwardFunction = jest.fn(); @@ -57,9 +58,7 @@ describe('DrawerHeadingBackwardButton - component', () => { it('should call class drawer on close button click', () => { const { store } = renderComponent('Title', 'value', { drawer: { - isOpen: true, - drawerName: 'search', - searchDrawerState: { currentStep: 0, selectedValue: { name: '', valueType: 'none' } }, + ...drawerSearchStepOneFixture, }, }); expect(store.getState().drawer.isOpen).toBe(true); diff --git a/src/shared/Icon/Icons/Pin.tsx b/src/shared/Icon/Icons/Pin.tsx index f0ee2edd45997180603d7ecedbab5fb24c78daaa..aa365e8415e48cff5c98a9a7f42908616ea4cb78 100644 --- a/src/shared/Icon/Icons/Pin.tsx +++ b/src/shared/Icon/Icons/Pin.tsx @@ -11,10 +11,7 @@ export const Pin = ({ className }: PinOrangeProps): JSX.Element => ( xmlns="http://www.w3.org/2000/svg" className={className} > - <path - d="M9 0C4.575 0 0 3.375 0 9C0 14.325 8.1 22.65 8.475 23.025C8.625 23.175 8.775 23.25 9 23.25C9.225 23.25 9.375 23.175 9.525 23.025C9.9 22.65 18 14.4 18 9C18 3.375 13.425 0 9 0ZM9 12C7.35 12 6 10.65 6 9C6 7.35 7.35 6 9 6C10.65 6 12 7.35 12 9C12 10.65 10.65 12 9 12Z" - fill="currentColor" - /> - <circle cx="9.0002" cy="8.99922" r="4.8" fill="currentColor" /> + <path d="M9 0C4.575 0 0 3.375 0 9C0 14.325 8.1 22.65 8.475 23.025C8.625 23.175 8.775 23.25 9 23.25C9.225 23.25 9.375 23.175 9.525 23.025C9.9 22.65 18 14.4 18 9C18 3.375 13.425 0 9 0ZM9 12C7.35 12 6 10.65 6 9C6 7.35 7.35 6 9 6C10.65 6 12 7.35 12 9C12 10.65 10.65 12 9 12Z" /> + <circle cx="9.0002" cy="8.99922" r="4.8" /> </svg> ); diff --git a/src/utils/assertNever.ts b/src/utils/assertNever.ts new file mode 100644 index 0000000000000000000000000000000000000000..7638cb3093223b9eb244428c686e99af4db8334d --- /dev/null +++ b/src/utils/assertNever.ts @@ -0,0 +1,3 @@ +export const assertNever = (value: never): never => { + throw new Error(`Unexpected value ${value}`); +}; diff --git a/tailwind.config.ts b/tailwind.config.ts index ce605cba16e8602fe0eccd1f17c8c26bdcc685d1..203d95ea697da1c18f58ee7b474cc1eaa31a228c 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -25,6 +25,8 @@ const config: Config = { cultured: '#f7f7f8', 'white-pearl': '#ffffff', divide: '#e1e0e6', + orange: '#f48c40', + purple: '#6400e3', }, height: { 'calc-drawer': 'calc(100% - 104px)',