diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx index c8a4826a1446507681ca673059c8e1f5c24d2b6b..cd116dd9e519875d002638bbea32f5a015d85781 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import { StoreType } from '@/redux/store'; import { InitialStoreState, @@ -6,8 +6,11 @@ import { } from '@/utils/testing/getReduxWrapperWithStore'; import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture'; import { Accordion } from '@/shared/Accordion'; +import { drawerSearchStepOneFixture } from '@/redux/drawer/drawerFixture'; import { ChemicalsAccordion } from './ChemicalsAccordion.component'; +const SECOND_STEP = 2; + const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); @@ -38,4 +41,47 @@ describe('DrugsAccordion - component', () => { }); expect(screen.getByText('Chemicals (Loading...)')).toBeInTheDocument(); }); + it('should navigate user to chemical results list after clicking button', async () => { + const { store } = renderComponent({ + chemicals: { + data: chemicalsFixture, + loading: 'succeeded', + error: { name: '', message: '' }, + }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + await act(() => { + navigationButton.click(); + }); + + const { + drawer: { + searchDrawerState: { stepType, selectedValue, currentStep }, + }, + } = store.getState(); + + expect(stepType).toBe('chemicals'); + expect(selectedValue).toBe(undefined); + expect(currentStep).toBe(SECOND_STEP); + }); + it('should disable navigation button when there is no chemicals', async () => { + renderComponent({ + chemicals: { data: [], loading: 'succeeded', error: { name: '', message: '' } }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + expect(navigationButton).toBeDisabled(); + }); + it('should disable navigation button when waiting for api response', async () => { + renderComponent({ + chemicals: { data: [], loading: 'pending', error: { name: '', message: '' } }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + expect(navigationButton).toBeDisabled(); + }); }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.tsx index 46ee6f7865e555e2d825ba238941400d09887788..36dc7c61cab95a7f43df3f3e5c7ead3bf8f40225 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion/ChemicalsAccordion.component.tsx @@ -1,21 +1,37 @@ +import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; import { numberOfChemicalsSelector, loadingChemicalsStatusSelector, } from '@/redux/chemicals/chemicals.selectors'; +import { displayChemicalsList } from '@/redux/drawer/drawer.slice'; +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppSelector } from '@/redux/hooks/useAppSelector'; import { AccordionItem, AccordionItemHeading, AccordionItemButton } from '@/shared/Accordion'; export const ChemicalsAccordion = (): JSX.Element => { + const dispatch = useAppDispatch(); const chemicalsNumber = useAppSelector(numberOfChemicalsSelector); - const drugsState = useAppSelector(loadingChemicalsStatusSelector); + const chemicalsState = useAppSelector(loadingChemicalsStatusSelector); + + const isPending = chemicalsState === 'pending'; + const isSucceeded = chemicalsState === 'succeeded'; + const isChemicalsEmpty = chemicalsNumber === SIZE_OF_EMPTY_ARRAY; + + const onAccordionClick = (): void => { + dispatch(displayChemicalsList()); + }; return ( <AccordionItem> <AccordionItemHeading> - <AccordionItemButton variant="non-expandable"> + <AccordionItemButton + variant="non-expandable" + onClick={onAccordionClick} + disabled={isPending || isChemicalsEmpty} + > Chemicals - {drugsState === 'pending' && ' (Loading...)'} - {drugsState === 'succeeded' && ` (${chemicalsNumber})`} + {isPending && ' (Loading...)'} + {isSucceeded && ` (${chemicalsNumber})`} </AccordionItemButton> </AccordionItemHeading> </AccordionItem> diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.test.tsx index c1d1f6fe7bcb63e15e2d4b2d6f7f0b0c11e987b4..ff2cc2dbf7fc8816c358cf5845a45fc269fb0840 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.test.tsx @@ -5,9 +5,12 @@ import { InitialStoreState, getReduxWrapperWithStore, } from '@/utils/testing/getReduxWrapperWithStore'; -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; +import { drawerSearchStepOneFixture } from '@/redux/drawer/drawerFixture'; import { DrugsAccordion } from './DrugsAccordion.component'; +const SECOND_STEP = 2; + const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); @@ -38,4 +41,47 @@ describe('DrugsAccordion - component', () => { }); expect(screen.getByText('Drugs (Loading...)')).toBeInTheDocument(); }); + it('should navigate user to chemical results list after clicking button', async () => { + const { store } = renderComponent({ + drugs: { + data: drugsFixture, + loading: 'succeeded', + error: { name: '', message: '' }, + }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + await act(() => { + navigationButton.click(); + }); + + const { + drawer: { + searchDrawerState: { stepType, selectedValue, currentStep }, + }, + } = store.getState(); + + expect(stepType).toBe('drugs'); + expect(selectedValue).toBe(undefined); + expect(currentStep).toBe(SECOND_STEP); + }); + it('should disable navigation button when there is no drugs', async () => { + renderComponent({ + drugs: { data: [], loading: 'succeeded', error: { name: '', message: '' } }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + expect(navigationButton).toBeDisabled(); + }); + it('should disable navigation button when waiting for api response', async () => { + renderComponent({ + drugs: { data: [], loading: 'pending', error: { name: '', message: '' } }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + expect(navigationButton).toBeDisabled(); + }); }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.tsx index 4053e7f637bfb49300b337b660f535cd3187bfb1..e92ba2bd1d565d99c5200c69bf73e70fd0820603 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion/DrugsAccordion.component.tsx @@ -11,6 +11,8 @@ export const DrugsAccordion = (): JSX.Element => { const dispatch = useAppDispatch(); const isPending = drugsState === 'pending'; + const isSucceeded = drugsState === 'succeeded'; + const isDrugsEmpty = drugsNumber === SIZE_OF_EMPTY_ARRAY; const onAccordionClick = (): void => { dispatch(displayDrugsList()); @@ -22,11 +24,11 @@ export const DrugsAccordion = (): JSX.Element => { <AccordionItemButton variant="non-expandable" onClick={onAccordionClick} - disabled={isPending || drugsNumber === SIZE_OF_EMPTY_ARRAY} + disabled={isPending || isDrugsEmpty} > Drugs - {drugsState === 'pending' && ' (Loading...)'} - {drugsState === 'succeeded' && ` (${drugsNumber})`} + {isPending && ' (Loading...)'} + {isSucceeded && ` (${drugsNumber})`} </AccordionItemButton> </AccordionItemHeading> </AccordionItem> diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.test.tsx index b3b10bf68e3f0478792e5e0add36aa5559fb70b6..334fce860b2ba396d967c6af477f6273946cb28f 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { act, render, screen } from '@testing-library/react'; import { StoreType } from '@/redux/store'; import { InitialStoreState, @@ -6,8 +6,11 @@ import { } from '@/utils/testing/getReduxWrapperWithStore'; import { mirnasFixture } from '@/models/fixtures/mirnasFixture'; import { Accordion } from '@/shared/Accordion'; +import { drawerSearchStepOneFixture } from '@/redux/drawer/drawerFixture'; import { MirnaAccordion } from './MirnaAccordion.component'; +const SECOND_STEP = 2; + const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); @@ -38,4 +41,47 @@ describe('DrugsAccordion - component', () => { }); expect(screen.getByText('MiRNA (Loading...)')).toBeInTheDocument(); }); + it('should navigate user to mirnas results list after clicking button', async () => { + const { store } = renderComponent({ + mirnas: { + data: mirnasFixture, + loading: 'succeeded', + error: { name: '', message: '' }, + }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + await act(() => { + navigationButton.click(); + }); + + const { + drawer: { + searchDrawerState: { stepType, selectedValue, currentStep }, + }, + } = store.getState(); + + expect(stepType).toBe('mirna'); + expect(selectedValue).toBe(undefined); + expect(currentStep).toBe(SECOND_STEP); + }); + it('should disable navigation button when there is no mirnas', async () => { + renderComponent({ + mirnas: { data: [], loading: 'succeeded', error: { name: '', message: '' } }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + expect(navigationButton).toBeDisabled(); + }); + it('should disable navigation button when waiting for api response', async () => { + renderComponent({ + mirnas: { data: [], loading: 'pending', error: { name: '', message: '' } }, + drawer: drawerSearchStepOneFixture, + }); + + const navigationButton = screen.getByTestId('accordion-item-button'); + expect(navigationButton).toBeDisabled(); + }); }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.tsx index e23bf05659a15086e88b15bdeeb3e3e83b0bcd6e..a6fdf742e0651d879fc8ebfcddbf25feb1683c93 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion/MirnaAccordion.component.tsx @@ -1,3 +1,6 @@ +import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; +import { displayMirnaList } from '@/redux/drawer/drawer.slice'; +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppSelector } from '@/redux/hooks/useAppSelector'; import { numberOfMirnasSelector, @@ -6,16 +9,28 @@ import { import { AccordionItem, AccordionItemHeading, AccordionItemButton } from '@/shared/Accordion'; export const MirnaAccordion = (): JSX.Element => { + const dispatch = useAppDispatch(); const mirnaNumber = useAppSelector(numberOfMirnasSelector); const mirnaState = useAppSelector(loadingMirnasStatusSelector); + const isPending = mirnaState === 'pending'; + const isSucceeded = mirnaState === 'succeeded'; + const isDrugsEmpty = mirnaNumber === SIZE_OF_EMPTY_ARRAY; + + const onAccordionClick = (): void => { + dispatch(displayMirnaList()); + }; return ( <AccordionItem> <AccordionItemHeading> - <AccordionItemButton variant="non-expandable"> + <AccordionItemButton + variant="non-expandable" + onClick={onAccordionClick} + disabled={isPending || isDrugsEmpty} + > MiRNA - {mirnaState === 'pending' && ' (Loading...)'} - {mirnaState === 'succeeded' && ` (${mirnaNumber})`} + {isPending && ' (Loading...)'} + {isSucceeded && ` (${mirnaNumber})`} </AccordionItemButton> </AccordionItemHeading> </AccordionItem> diff --git a/src/redux/drawer/drawer.reducers.test.ts b/src/redux/drawer/drawer.reducers.test.ts index 58f37fa1eeaf89d1a9d41e03ad6640e5a787675e..ff8f02c239dbf2cc5a72c41ae3bd1001f856af12 100644 --- a/src/redux/drawer/drawer.reducers.test.ts +++ b/src/redux/drawer/drawer.reducers.test.ts @@ -4,6 +4,7 @@ import type { ToolkitStore } from '@reduxjs/toolkit/dist/configureStore'; import { drugFixture } from '@/models/fixtures/drugFixtures'; import drawerReducer, { closeDrawer, + displayChemicalsList, displayDrugsList, displayEntityDetails, displayGroupedSearchResults, @@ -107,6 +108,19 @@ describe('drawer reducer', () => { expect(stepType).toEqual('drugs'); }); + it('should update the store after displayChemicalsList action', async () => { + const { type } = await store.dispatch(displayChemicalsList()); + const { + drawerName, + searchDrawerState: { currentStep, stepType }, + } = store.getState().drawer; + + expect(type).toBe('drawer/displayChemicalsList'); + expect(drawerName).toBe('search'); + expect(currentStep).toBe(STEP.SECOND); + expect(stepType).toEqual('chemicals'); + }); + it('should update the store after displayGroupedSearchResults action', async () => { const { type } = await store.dispatch(displayGroupedSearchResults()); const { diff --git a/src/redux/drawer/drawer.reducers.ts b/src/redux/drawer/drawer.reducers.ts index 84714e5f3660ed6edaffcf4399d5600ab96b5cc3..e68271b783561b56761808589cd2785eb126548f 100644 --- a/src/redux/drawer/drawer.reducers.ts +++ b/src/redux/drawer/drawer.reducers.ts @@ -28,6 +28,18 @@ export const displayDrugsListReducer = (state: DrawerState): void => { state.searchDrawerState.stepType = 'drugs'; }; +export const displayChemicalsListReducer = (state: DrawerState): void => { + state.drawerName = 'search'; + state.searchDrawerState.currentStep = STEP.SECOND; + state.searchDrawerState.stepType = 'chemicals'; +}; + +export const displayMirnaListReducer = (state: DrawerState): void => { + state.drawerName = 'search'; + state.searchDrawerState.currentStep = STEP.SECOND; + state.searchDrawerState.stepType = 'mirna'; +}; + export const displayGroupedSearchResultsReducer = (state: DrawerState): void => { state.searchDrawerState.currentStep = STEP.FIRST; state.searchDrawerState.stepType = 'none'; diff --git a/src/redux/drawer/drawer.slice.ts b/src/redux/drawer/drawer.slice.ts index 153ee4cca689e3c9660b155229736eadf8e16fa6..3deb088ddb094612b0e6086781c88b26026d4f31 100644 --- a/src/redux/drawer/drawer.slice.ts +++ b/src/redux/drawer/drawer.slice.ts @@ -2,9 +2,11 @@ import { DrawerState } from '@/redux/drawer/drawer.types'; import { createSlice } from '@reduxjs/toolkit'; import { closeDrawerReducer, + displayChemicalsListReducer, displayDrugsListReducer, displayEntityDetailsReducer, displayGroupedSearchResultsReducer, + displayMirnaListReducer, openDrawerReducer, openSearchDrawerReducer, } from './drawer.reducers'; @@ -27,6 +29,8 @@ const drawerSlice = createSlice({ openSearchDrawer: openSearchDrawerReducer, closeDrawer: closeDrawerReducer, displayDrugsList: displayDrugsListReducer, + displayChemicalsList: displayChemicalsListReducer, + displayMirnaList: displayMirnaListReducer, displayGroupedSearchResults: displayGroupedSearchResultsReducer, displayEntityDetails: displayEntityDetailsReducer, }, @@ -37,6 +41,8 @@ export const { openSearchDrawer, closeDrawer, displayDrugsList, + displayChemicalsList, + displayMirnaList, displayGroupedSearchResults, displayEntityDetails, } = drawerSlice.actions; diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx index d4a56484635760d1a5de258f993969a1878d0655..d932d2b580323d5eb5902438deb9ba435ddc247f 100644 --- a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx +++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx @@ -25,6 +25,7 @@ export const AccordionItemButton = ({ disabled={disabled} className="flex w-full flex-row flex-nowrap justify-between text-sm" type="button" + data-testid="accordion-item-button" > {children} {ButtonIcon}