From 4323c1dcfc76d82b604297b45ead90043595182d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tadeusz=20Miesi=C4=85c?= <tadeusz.miesiac@gmail.com>
Date: Wed, 11 Oct 2023 20:41:29 +0800
Subject: [PATCH] feat(drugs results): display drugs search results

---
 .../Map/Drawer/Drawer.component.test.tsx      | 12 ++--
 .../Map/Drawer/Drawer.component.tsx           |  8 +--
 .../BioEntitiesAccordion.component.tsx        | 49 ++++++++-------
 .../DrugsAccordion.component.test.tsx         | 41 +++++++++++++
 .../DrugsAccordion.component.tsx              | 20 +++++++
 .../DrugsAccordion/index.ts                   |  1 +
 .../SearchDrawerContent.component.tsx         |  7 ++-
 .../AccordionItemButton.component.tsx         | 32 ++++++++--
 .../testing/getReduxWrapperWithStore.tsx      | 59 +++++++++++++++++++
 9 files changed, 185 insertions(+), 44 deletions(-)
 create mode 100644 src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx
 create mode 100644 src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx
 create mode 100644 src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts
 create mode 100644 src/utils/testing/getReduxWrapperWithStore.tsx

diff --git a/src/components/Map/Drawer/Drawer.component.test.tsx b/src/components/Map/Drawer/Drawer.component.test.tsx
index a0663188..20c5630b 100644
--- a/src/components/Map/Drawer/Drawer.component.test.tsx
+++ b/src/components/Map/Drawer/Drawer.component.test.tsx
@@ -1,13 +1,11 @@
-import { ToolkitStoreWithSingleSlice } from '@/utils/createStoreInstanceUsingSliceReducer';
-import { DrawerState } from '@/redux/drawer/drawer.types';
-import { getReduxWrapperUsingSliceReducer } from '@/utils/testing/getReduxWrapperUsingSliceReducer';
 import { screen, render, act, fireEvent } from '@testing-library/react';
-import drawerReducer, { openDrawer } from '@/redux/drawer/drawer.slice';
+import { openDrawer } from '@/redux/drawer/drawer.slice';
+import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
+import { StoreType } from '@/redux/store';
 import { Drawer } from './Drawer.component';
 
-const renderComponent = (): { store: ToolkitStoreWithSingleSlice<DrawerState> } => {
-  const { Wrapper, store } = getReduxWrapperUsingSliceReducer('drawer', drawerReducer);
-
+const renderComponent = (): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore();
   return (
     render(
       <Wrapper>
diff --git a/src/components/Map/Drawer/Drawer.component.tsx b/src/components/Map/Drawer/Drawer.component.tsx
index 4e692262..a08fd813 100644
--- a/src/components/Map/Drawer/Drawer.component.tsx
+++ b/src/components/Map/Drawer/Drawer.component.tsx
@@ -1,8 +1,8 @@
 import dynamic from 'next/dynamic';
 import { twMerge } from 'tailwind-merge';
 import { useAppSelector } from '@/redux/hooks/useAppSelector';
-import { drawerDataSelector } from '@/redux/drawer/drawer.selectors';
 import { DRAWER_ROLE } from '@/components/Map/Drawer/Drawer.constants';
+import { drawerSelector } from '@/redux/drawer/drawer.selectors';
 
 const SearchDrawerContent = dynamic(
   async () =>
@@ -15,17 +15,17 @@ const SearchDrawerContent = dynamic(
 );
 
 export const Drawer = (): JSX.Element => {
-  const { open, drawerName } = useAppSelector(drawerDataSelector);
+  const { isOpen, drawerName } = useAppSelector(drawerSelector);
 
   return (
     <div
       className={twMerge(
         'absolute left-[88px] top-[104px] z-10 h-calc-drawer w-[432px] -translate-x-full transform bg-white-pearl text-font-500 transition-all duration-500',
-        open && 'translate-x-0',
+        isOpen && 'translate-x-0',
       )}
       role={DRAWER_ROLE}
     >
-      {open && drawerName === 'search' && <SearchDrawerContent />}
+      {isOpen && drawerName === 'search' && <SearchDrawerContent />}
       {/* other drawers comes here, should use dynamic import */}
     </div>
   );
diff --git a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx
index ff006e6b..6442eca6 100644
--- a/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx
+++ b/src/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion/BioEntitiesAccordion.component.tsx
@@ -1,5 +1,4 @@
 import {
-  Accordion,
   AccordionItem,
   AccordionItemButton,
   AccordionItemPanel,
@@ -11,30 +10,28 @@ import { BioEntitiesSubmapItem } from '@/components/Map/Drawer/SearchDrawerConte
 export const BioEntitiesAccordion = (): JSX.Element => {
   const entity = { mapName: 'main map', numberOfEntities: 21 };
   return (
-    <Accordion allowZeroExpanded>
-      <AccordionItem>
-        <AccordionItemHeading>
-          <AccordionItemButton>Content (2137)</AccordionItemButton>
-        </AccordionItemHeading>
-        <AccordionItemPanel className="">
-          <BioEntitiesSubmapItem
-            mapName={entity.mapName}
-            numberOfEntities={entity.numberOfEntities}
-          />
-          <BioEntitiesSubmapItem
-            mapName={entity.mapName}
-            numberOfEntities={entity.numberOfEntities}
-          />
-          <BioEntitiesSubmapItem
-            mapName={entity.mapName}
-            numberOfEntities={entity.numberOfEntities}
-          />
-          <BioEntitiesSubmapItem
-            mapName={entity.mapName}
-            numberOfEntities={entity.numberOfEntities}
-          />
-        </AccordionItemPanel>
-      </AccordionItem>
-    </Accordion>
+    <AccordionItem>
+      <AccordionItemHeading>
+        <AccordionItemButton>Content (2137)</AccordionItemButton>
+      </AccordionItemHeading>
+      <AccordionItemPanel className="">
+        <BioEntitiesSubmapItem
+          mapName={entity.mapName}
+          numberOfEntities={entity.numberOfEntities}
+        />
+        <BioEntitiesSubmapItem
+          mapName={entity.mapName}
+          numberOfEntities={entity.numberOfEntities}
+        />
+        <BioEntitiesSubmapItem
+          mapName={entity.mapName}
+          numberOfEntities={entity.numberOfEntities}
+        />
+        <BioEntitiesSubmapItem
+          mapName={entity.mapName}
+          numberOfEntities={entity.numberOfEntities}
+        />
+      </AccordionItemPanel>
+    </AccordionItem>
   );
 };
diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx
new file mode 100644
index 00000000..5abedd8f
--- /dev/null
+++ b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.test.tsx
@@ -0,0 +1,41 @@
+import { render, screen } from '@testing-library/react';
+import { StoreType } from '@/redux/store';
+import {
+  InitialStoreState,
+  getReduxWrapperWithStore,
+} from '@/utils/testing/getReduxWrapperWithStore';
+import { drugsFixture } from '@/models/fixtures/drugFixtures';
+import { Accordion } from '@/shared/Accordion';
+import { DrugsAccordion } from './DrugsAccordion.component';
+
+const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
+  const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
+
+  return (
+    render(
+      <Wrapper>
+        <Accordion>
+          <DrugsAccordion />
+        </Accordion>
+      </Wrapper>,
+    ),
+    {
+      store,
+    }
+  );
+};
+
+describe('DrugsAccordion - component', () => {
+  it('should display drugs number after succesfull drug search', () => {
+    renderComponent({
+      drugs: { data: drugsFixture, loading: 'succeeded', error: { name: '', message: '' } },
+    });
+    expect(screen.getByText('Drugs (2)')).toBeInTheDocument();
+  });
+  it('should display loading indicator while waiting for drug search response', () => {
+    renderComponent({
+      drugs: { data: [], loading: 'pending', error: { name: '', message: '' } },
+    });
+    expect(screen.getByText('Drugs (Loading...)')).toBeInTheDocument();
+  });
+});
diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx
new file mode 100644
index 00000000..af143875
--- /dev/null
+++ b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/DrugsAccordion.component.tsx
@@ -0,0 +1,20 @@
+import { loadingDrugsStatusSelector, numberOfDrugsSelector } from '@/redux/drugs/drugs.selectors';
+import { useAppSelector } from '@/redux/hooks/useAppSelector';
+import { AccordionItem, AccordionItemHeading, AccordionItemButton } from '@/shared/Accordion';
+
+export const DrugsAccordion = (): JSX.Element => {
+  const drugsNumber = useAppSelector(numberOfDrugsSelector);
+  const drugsState = useAppSelector(loadingDrugsStatusSelector);
+
+  return (
+    <AccordionItem>
+      <AccordionItemHeading>
+        <AccordionItemButton variant="non-expandable">
+          Drugs
+          {drugsState === 'pending' && ' (Loading...)'}
+          {drugsState === 'succeeded' && ` (${drugsNumber})`}
+        </AccordionItemButton>
+      </AccordionItemHeading>
+    </AccordionItem>
+  );
+};
diff --git a/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts
new file mode 100644
index 00000000..36be6a7a
--- /dev/null
+++ b/src/components/Map/Drawer/SearchDrawerContent/DrugsAccordion/index.ts
@@ -0,0 +1 @@
+export { DrugsAccordion } from './DrugsAccordion.component';
diff --git a/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx
index ad1b350c..ea53111c 100644
--- a/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx
+++ b/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx
@@ -1,7 +1,9 @@
 import { BioEntitiesAccordion } from '@/components/Map/Drawer/SearchDrawerContent/BioEntitiesAccordion';
+import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerContent/DrugsAccordion';
 import { closeDrawer } from '@/redux/drawer/drawer.slice';
 import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
 import { IconButton } from '@/shared/IconButton';
+import { Accordion } from '@/shared/Accordion';
 
 export const CLOSE_BUTTON_ROLE = 'close-drawer-button';
 
@@ -28,7 +30,10 @@ export const SearchDrawerContent = (): JSX.Element => {
         />
       </div>
       <div className="px-6">
-        <BioEntitiesAccordion />
+        <Accordion allowZeroExpanded>
+          <BioEntitiesAccordion />
+          <DrugsAccordion />
+        </Accordion>
       </div>
     </div>
   );
diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx
index 32942ae9..dffa5b9f 100644
--- a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx
+++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx
@@ -1,14 +1,34 @@
+/* eslint-disable react/no-multi-comp */
 import { Icon } from '@/shared/Icon';
 import { AccordionItemButton as AIB } from 'react-accessible-accordion';
 import './AccordionItemButton.style.css';
 
+type Variant = 'expandable' | 'non-expandable';
 interface AccordionItemButtonProps {
   children: React.ReactNode;
+  variant?: Variant;
 }
 
-export const AccordionItemButton = ({ children }: AccordionItemButtonProps): JSX.Element => (
-  <AIB className="accordion-button flex flex-row flex-nowrap justify-between">
-    {children}
-    <Icon name="chevron-down" className="arrow-button h-6 w-6 fill-font-500" />
-  </AIB>
-);
+const getIcon = (variant: Variant): JSX.Element => {
+  const variantsIcons: Record<Variant, JSX.Element> = {
+    expandable: <Icon name="chevron-down" className="arrow-button h-6 w-6 fill-font-500" />,
+    'non-expandable': <Icon name="chevron-right" className="h-6 w-6 fill-font-500" />,
+  };
+
+  return variantsIcons[variant];
+};
+
+export const AccordionItemButton = ({
+  children,
+  variant = 'expandable',
+}: AccordionItemButtonProps): JSX.Element => {
+  const ButtonIcon = getIcon(variant);
+
+  return (
+    <AIB className="accordion-button flex flex-row flex-nowrap justify-between">
+      {children}
+      {/* <Icon name="chevron-down" className="arrow-button h-6 w-6 fill-font-500" /> */}
+      {ButtonIcon}
+    </AIB>
+  );
+};
diff --git a/src/utils/testing/getReduxWrapperWithStore.tsx b/src/utils/testing/getReduxWrapperWithStore.tsx
new file mode 100644
index 00000000..37e3cd5a
--- /dev/null
+++ b/src/utils/testing/getReduxWrapperWithStore.tsx
@@ -0,0 +1,59 @@
+import { store } from '@/redux/store';
+import { configureStore } from '@reduxjs/toolkit';
+import { Provider } from 'react-redux';
+import bioEntityContentsReducer from '@/redux/bioEntityContents/bioEntityContents.slice';
+import chemicalsReducer from '@/redux/chemicals/chemicals.slice';
+import drawerReducer from '@/redux/drawer/drawer.slice';
+import drugsReducer from '@/redux/drugs/drugs.slice';
+import mirnasReducer from '@/redux/mirnas/mirnas.slice';
+import projectReducer from '@/redux/project/project.slice';
+import searchReducer from '@/redux/search/search.slice';
+import { SearchState } from '@/redux/search/search.types';
+import { ProjectState } from '@/redux/project/project.types';
+import { DrugsState } from '@/redux/drugs/drugs.types';
+import { MirnasState } from '@/redux/mirnas/mirnas.types';
+import { ChemicalsState } from '@/redux/chemicals/chemicals.types';
+import { BioEntityContentsState } from '@/redux/bioEntityContents/bioEntityContents.types';
+import { DrawerState } from '@/redux/drawer/drawer.types';
+
+interface WrapperProps {
+  children: React.ReactNode;
+}
+
+export type InitialStoreState = {
+  search?: SearchState;
+  project?: ProjectState;
+  drugs?: DrugsState;
+  mirnas?: MirnasState;
+  chemicals?: ChemicalsState;
+  bioEntityContents?: BioEntityContentsState;
+  drawer?: DrawerState;
+};
+
+type GetReduxWrapperUsingSliceReducer = (initialState?: InitialStoreState) => {
+  Wrapper: ({ children }: WrapperProps) => JSX.Element;
+  store: typeof store;
+};
+
+export const getReduxWrapperWithStore: GetReduxWrapperUsingSliceReducer = (
+  preloadedState: InitialStoreState = {},
+) => {
+  const testStore = configureStore({
+    reducer: {
+      search: searchReducer,
+      project: projectReducer,
+      drugs: drugsReducer,
+      mirnas: mirnasReducer,
+      chemicals: chemicalsReducer,
+      bioEntityContents: bioEntityContentsReducer,
+      drawer: drawerReducer,
+    },
+    preloadedState,
+  });
+
+  const Wrapper = ({ children }: WrapperProps): JSX.Element => (
+    <Provider store={testStore}>{children}</Provider>
+  );
+
+  return { Wrapper, store: testStore };
+};
-- 
GitLab