From efc617d00936f6a04976b96fb0a57fe9e385cd48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadeusz=20Miesi=C4=85c?= <tadeusz.miesiac@gmail.com> Date: Thu, 19 Oct 2023 14:31:09 +0800 Subject: [PATCH] feat(results list): connected drugs drawer to results list --- .eslintrc.json | 2 +- .../TopBar/SearchBar/SearchBar.component.tsx | 5 +- .../Map/Drawer/Drawer.component.test.tsx | 6 +- .../Map/Drawer/Drawer.component.tsx | 13 +- .../Results/PinsList/PinsList.component.tsx | 14 -- .../Results/PinsList/PinsList.types.tsx | 3 - .../PinsListItem/PinsListItem.component.tsx | 13 -- .../Results/Results.component.tsx | 27 ---- .../SearchDrawerContent/Results/index.ts | 1 - .../Map/Drawer/SearchDrawerContent/index.ts | 1 - .../BioEntitiesAccordion.component.tsx | 2 +- .../BioEntitiesSubmapItem.component.test.tsx | 0 .../BioEntitiesSubmapItem.component.tsx | 0 .../BioEntitiesSubmapItem/index.ts | 0 .../BioEntitiesAccordion/index.ts | 0 .../ChemicalsAccordion.component.test.tsx | 0 .../ChemicalsAccordion.component.tsx | 0 .../ChemicalsAccordion/index.ts | 0 .../DrugsAccordion.component.test.tsx | 0 .../DrugsAccordion.component.tsx | 16 ++- .../DrugsAccordion/index.ts | 0 .../GroupedSearchResults.component.tsx} | 12 +- .../MirnaAccordion.component.test.tsx | 0 .../MirnaAccordion.component.tsx | 0 .../MirnaAccordion/index.ts | 0 .../GroupedSearchResults/index.ts | 1 + .../PinsList/PinsList.component.test.tsx | 60 +++++++++ .../PinsList/PinsList.component.tsx | 31 +++++ .../ResultsList/PinsList/PinsList.types.tsx | 9 ++ .../PinsListItem/PinsListItem.component.tsx | 18 +++ .../PinsListItem.component.utils.ts | 19 +++ .../PinsList/PinsListItem/index.ts | 0 .../ResultsList}/PinsList/index.ts | 0 .../ResultsList.component.test.tsx | 86 ++++++++++++ .../ResultsList/ResultsList.component.tsx | 29 ++++ .../SearchDrawerWrapper/ResultsList/index.ts | 1 + .../SearchDrawerWrapper.component.test.tsx | 47 +++---- .../SearchDrawerWrapper.component.tsx | 20 +-- .../searchDrawer.ts} | 0 .../fixtures/bioEntityContentsFixture.ts | 5 + src/models/fixtures/drugFixtures.ts | 5 + src/redux/drawer/drawer.reducers.test.ts | 125 ++++++++++-------- src/redux/drawer/drawer.reducers.ts | 38 ++++-- src/redux/drawer/drawer.selectors.ts | 34 ++++- src/redux/drawer/drawer.slice.ts | 28 ++-- src/redux/drawer/drawer.types.ts | 32 +---- src/redux/drawer/drawerFixture.ts | 31 +++++ .../AccordionItemButton.component.tsx | 19 ++- .../AccordionItemButton.style.css | 2 +- ...erHeadingBackwardButton.component.test.tsx | 5 +- src/shared/Icon/Icons/Pin.tsx | 7 +- src/utils/assertNever.ts | 3 + tailwind.config.ts | 2 + 53 files changed, 523 insertions(+), 249 deletions(-) delete mode 100644 src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsList.component.tsx delete mode 100644 src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsList.types.tsx delete mode 100644 src/components/Map/Drawer/SearchDrawerContent/Results/PinsList/PinsListItem/PinsListItem.component.tsx delete mode 100644 src/components/Map/Drawer/SearchDrawerContent/Results/Results.component.tsx delete mode 100644 src/components/Map/Drawer/SearchDrawerContent/Results/index.ts delete mode 100644 src/components/Map/Drawer/SearchDrawerContent/index.ts rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx (92%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.test.tsx (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/BioEntitiesAccordion/BioEntitiesSubmapItem/BioEntitiesSubmapItem.component.tsx (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/BioEntitiesAccordion/BioEntitiesSubmapItem/index.ts (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/BioEntitiesAccordion/index.ts (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/ChemicalsAccordion/ChemicalsAccordion.component.tsx (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/ChemicalsAccordion/index.ts (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/DrugsAccordion/DrugsAccordion.component.test.tsx (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/DrugsAccordion/DrugsAccordion.component.tsx (57%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/DrugsAccordion/index.ts (100%) rename src/components/Map/Drawer/{SearchDrawerContent/SearchDrawerContent.component.tsx => SearchDrawerWrapper/GroupedSearchResults/GroupedSearchResults.component.tsx} (79%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/MirnaAccordion/MirnaAccordion.component.test.tsx (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/MirnaAccordion/MirnaAccordion.component.tsx (100%) rename src/components/Map/Drawer/{SearchDrawerContent => SearchDrawerWrapper/GroupedSearchResults}/MirnaAccordion/index.ts (100%) create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/index.ts create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.test.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.types.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/PinsListItem.component.utils.ts rename src/components/Map/Drawer/{SearchDrawerContent/Results => SearchDrawerWrapper/ResultsList}/PinsList/PinsListItem/index.ts (100%) rename src/components/Map/Drawer/{SearchDrawerContent/Results => SearchDrawerWrapper/ResultsList}/PinsList/index.ts (100%) create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.test.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/index.ts rename src/{components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.constants.ts => constants/searchDrawer.ts} (100%) create mode 100644 src/redux/drawer/drawerFixture.ts create mode 100644 src/utils/assertNever.ts diff --git a/.eslintrc.json b/.eslintrc.json index f7df93bc..e60a21f0 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 66f5fc8f..4cce8b0a 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 1741384a..26c7bd98 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 52f4a1d5..6415d402 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 2678ebb1..00000000 --- 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 ce02ca44..00000000 --- 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 c6eba919..00000000 --- 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 89b9cd60..00000000 --- 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 0b2da8f8..00000000 --- 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 72074d52..00000000 --- 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 6442eca6..1fecf9a7 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 af143875..4053e7f6 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 7f0cc18b..29a6076e 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 00000000..99f53f8e --- /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 00000000..676b58b4 --- /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 00000000..caa8ea04 --- /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 00000000..5369b84d --- /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 00000000..6b8e36b7 --- /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 00000000..873de967 --- /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 00000000..7e310b6b --- /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 00000000..25000b8f --- /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 00000000..5ca1e342 --- /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 19c588d7..63229511 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 9ce500cf..23aa9763 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 adc441d3..fec6a713 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 44c60b79..b7d74539 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 d7bf3ced..58f37fa1 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 e205e9a4..84714e5f 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 a622a1f6..b928a6bd 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 cbbe5a33..153ee4cc 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 404d0fc2..e39ff163 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 00000000..46c1896a --- /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 09603e06..d4a56484 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 ee300f20..9da947bb 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 e35e2049..38cee7f2 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 f0ee2edd..aa365e84 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 00000000..7638cb30 --- /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 ce605cba..203d95ea 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)', -- GitLab