From cd14e79ab4024b3b852df8ae8afa5f90ebed4833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadeusz=20Miesi=C4=85c?= <tadeusz.miesiac@gmail.com> Date: Mon, 16 Oct 2023 18:07:36 +0800 Subject: [PATCH] feat(chemicals,mirna accordion): added chemicals and mirna accordion --- .../ChemicalsAccordion.component.test.tsx | 41 +++++++++++++++++ .../ChemicalsAccordion.component.tsx | 23 ++++++++++ .../ChemicalsAccordion/index.ts | 1 + .../MirnaAccordion.component.test.tsx | 41 +++++++++++++++++ .../MirnaAccordion.component.tsx | 23 ++++++++++ .../MirnaAccordion/index.ts | 1 + .../SearchDrawerContent.component.tsx | 4 ++ src/redux/chemicals/chemicals.selectors.ts | 6 +++ src/redux/mirnas/mirnas.selectors.ts | 5 ++ src/redux/store.ts | 1 - ...erHeadingBackwardButton.component.test.tsx | 11 +++-- src/utils/getReduxWrapperWithStore.tsx | 46 ------------------- .../testing/getReduxWrapperWithStore.tsx | 23 ++-------- 13 files changed, 157 insertions(+), 69 deletions(-) create mode 100644 src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/index.ts create mode 100644 src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.test.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/index.ts delete mode 100644 src/utils/getReduxWrapperWithStore.tsx diff --git a/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.test.tsx new file mode 100644 index 00000000..c8a4826a --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.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 { chemicalsFixture } from '@/models/fixtures/chemicalsFixture'; +import { Accordion } from '@/shared/Accordion'; +import { ChemicalsAccordion } from './ChemicalsAccordion.component'; + +const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { + const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); + + return ( + render( + <Wrapper> + <Accordion> + <ChemicalsAccordion /> + </Accordion> + </Wrapper>, + ), + { + store, + } + ); +}; + +describe('DrugsAccordion - component', () => { + it('should display drugs number after succesfull chemicals search', () => { + renderComponent({ + chemicals: { data: chemicalsFixture, loading: 'succeeded', error: { name: '', message: '' } }, + }); + expect(screen.getByText('Chemicals (2)')).toBeInTheDocument(); + }); + it('should display loading indicator while waiting for chemicals search response', () => { + renderComponent({ + chemicals: { data: [], loading: 'pending', error: { name: '', message: '' } }, + }); + expect(screen.getByText('Chemicals (Loading...)')).toBeInTheDocument(); + }); +}); diff --git a/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.tsx new file mode 100644 index 00000000..46ee6f78 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/ChemicalsAccordion.component.tsx @@ -0,0 +1,23 @@ +import { + numberOfChemicalsSelector, + loadingChemicalsStatusSelector, +} from '@/redux/chemicals/chemicals.selectors'; +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { AccordionItem, AccordionItemHeading, AccordionItemButton } from '@/shared/Accordion'; + +export const ChemicalsAccordion = (): JSX.Element => { + const chemicalsNumber = useAppSelector(numberOfChemicalsSelector); + const drugsState = useAppSelector(loadingChemicalsStatusSelector); + + return ( + <AccordionItem> + <AccordionItemHeading> + <AccordionItemButton variant="non-expandable"> + Chemicals + {drugsState === 'pending' && ' (Loading...)'} + {drugsState === 'succeeded' && ` (${chemicalsNumber})`} + </AccordionItemButton> + </AccordionItemHeading> + </AccordionItem> + ); +}; diff --git a/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/index.ts b/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/index.ts new file mode 100644 index 00000000..9fc44924 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/ChemicalsAccordion/index.ts @@ -0,0 +1 @@ +export { ChemicalsAccordion } from './ChemicalsAccordion.component'; diff --git a/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.test.tsx new file mode 100644 index 00000000..b3b10bf6 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.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 { mirnasFixture } from '@/models/fixtures/mirnasFixture'; +import { Accordion } from '@/shared/Accordion'; +import { MirnaAccordion } from './MirnaAccordion.component'; + +const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { + const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); + + return ( + render( + <Wrapper> + <Accordion> + <MirnaAccordion /> + </Accordion> + </Wrapper>, + ), + { + store, + } + ); +}; + +describe('DrugsAccordion - component', () => { + it('should display drugs number after succesfull chemicals search', () => { + renderComponent({ + mirnas: { data: mirnasFixture, loading: 'succeeded', error: { name: '', message: '' } }, + }); + expect(screen.getByText('MiRNA (2)')).toBeInTheDocument(); + }); + it('should display loading indicator while waiting for chemicals search response', () => { + renderComponent({ + mirnas: { data: [], loading: 'pending', error: { name: '', message: '' } }, + }); + expect(screen.getByText('MiRNA (Loading...)')).toBeInTheDocument(); + }); +}); diff --git a/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.tsx new file mode 100644 index 00000000..e23bf056 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/MirnaAccordion.component.tsx @@ -0,0 +1,23 @@ +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { + numberOfMirnasSelector, + loadingMirnasStatusSelector, +} from '@/redux/mirnas/mirnas.selectors'; +import { AccordionItem, AccordionItemHeading, AccordionItemButton } from '@/shared/Accordion'; + +export const MirnaAccordion = (): JSX.Element => { + const mirnaNumber = useAppSelector(numberOfMirnasSelector); + const mirnaState = useAppSelector(loadingMirnasStatusSelector); + + return ( + <AccordionItem> + <AccordionItemHeading> + <AccordionItemButton variant="non-expandable"> + MiRNA + {mirnaState === 'pending' && ' (Loading...)'} + {mirnaState === 'succeeded' && ` (${mirnaNumber})`} + </AccordionItemButton> + </AccordionItemHeading> + </AccordionItem> + ); +}; diff --git a/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/index.ts b/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/index.ts new file mode 100644 index 00000000..69f2736c --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerContent/MirnaAccordion/index.ts @@ -0,0 +1 @@ +export { MirnaAccordion } from './MirnaAccordion.component'; diff --git a/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx b/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx index ea53111c..7f0cc18b 100644 --- a/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerContent/SearchDrawerContent.component.tsx @@ -1,5 +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 { closeDrawer } from '@/redux/drawer/drawer.slice'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { IconButton } from '@/shared/IconButton'; @@ -33,6 +35,8 @@ export const SearchDrawerContent = (): JSX.Element => { <Accordion allowZeroExpanded> <BioEntitiesAccordion /> <DrugsAccordion /> + <ChemicalsAccordion /> + <MirnaAccordion /> </Accordion> </div> </div> diff --git a/src/redux/chemicals/chemicals.selectors.ts b/src/redux/chemicals/chemicals.selectors.ts index b6c7cb1c..d0cdd9b7 100644 --- a/src/redux/chemicals/chemicals.selectors.ts +++ b/src/redux/chemicals/chemicals.selectors.ts @@ -1,9 +1,15 @@ import { rootSelector } from '@/redux/root/root.selectors'; import { createSelector } from '@reduxjs/toolkit'; +const SIZE_OF_EMPTY_ARRAY = 0; + export const chemicalsSelector = createSelector(rootSelector, state => state.chemicals); export const loadingChemicalsStatusSelector = createSelector( chemicalsSelector, state => state.loading, ); + +export const numberOfChemicalsSelector = createSelector(chemicalsSelector, state => + state.data ? state.data.length : SIZE_OF_EMPTY_ARRAY, +); diff --git a/src/redux/mirnas/mirnas.selectors.ts b/src/redux/mirnas/mirnas.selectors.ts index 5344f037..59af89ec 100644 --- a/src/redux/mirnas/mirnas.selectors.ts +++ b/src/redux/mirnas/mirnas.selectors.ts @@ -1,6 +1,11 @@ import { rootSelector } from '@/redux/root/root.selectors'; import { createSelector } from '@reduxjs/toolkit'; +const SIZE_OF_EMPTY_ARRAY = 0; + export const mirnasSelector = createSelector(rootSelector, state => state.mirnas); export const loadingMirnasStatusSelector = createSelector(mirnasSelector, state => state.loading); +export const numberOfMirnasSelector = createSelector(mirnasSelector, state => + state.data ? state.data.length : SIZE_OF_EMPTY_ARRAY, +); diff --git a/src/redux/store.ts b/src/redux/store.ts index 194e3db0..841a79b8 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -27,4 +27,3 @@ export type StoreType = typeof store; export type RootState = ReturnType<typeof store.getState>; // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} export type AppDispatch = typeof store.dispatch; -export type StoreType = typeof store; diff --git a/src/shared/DrawerHeadingBackwardButton/DrawerHeadingBackwardButton.component.test.tsx b/src/shared/DrawerHeadingBackwardButton/DrawerHeadingBackwardButton.component.test.tsx index c6ef266e..23e0ad5d 100644 --- a/src/shared/DrawerHeadingBackwardButton/DrawerHeadingBackwardButton.component.test.tsx +++ b/src/shared/DrawerHeadingBackwardButton/DrawerHeadingBackwardButton.component.test.tsx @@ -1,5 +1,8 @@ import { StoreType } from '@/redux/store'; -import { InitialStoreState, getReduxWrapperWithStore } from '@/utils/getReduxWrapperWithStore'; +import { + InitialStoreState, + getReduxWrapperWithStore, +} from '@/utils/testing/getReduxWrapperWithStore'; import { render, screen } from '@testing-library/react'; import { DrawerHeadingBackwardButton } from './DrawerHeadingBackwardButton.component'; @@ -53,13 +56,13 @@ describe('DrawerHeadingBackwardButton - component', () => { it('should call class drawer on close button click', () => { const { store } = renderComponent('Title', 'value', { - drawer: { open: true, drawerName: 'search' }, + drawer: { isOpen: true, drawerName: 'search' }, }); - expect(store.getState().drawer.open).toBe(true); + expect(store.getState().drawer.isOpen).toBe(true); const closeButton = screen.getByRole('close-drawer-button'); closeButton.click(); - expect(store.getState().drawer.open).toBe(false); + expect(store.getState().drawer.isOpen).toBe(false); }); }); diff --git a/src/utils/getReduxWrapperWithStore.tsx b/src/utils/getReduxWrapperWithStore.tsx deleted file mode 100644 index cf6097bd..00000000 --- a/src/utils/getReduxWrapperWithStore.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { RootState, StoreType } 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 modelsReducer from '@/redux/models/models.slice'; - -interface WrapperProps { - children: React.ReactNode; -} - -export type InitialStoreState = Partial<RootState>; - -type GetReduxWrapperUsingSliceReducer = (initialState?: InitialStoreState) => { - Wrapper: ({ children }: WrapperProps) => JSX.Element; - store: StoreType; -}; - -export const getReduxWrapperWithStore: GetReduxWrapperUsingSliceReducer = ( - preloadedState: InitialStoreState = {}, -) => { - const testStore = configureStore({ - reducer: { - search: searchReducer, - project: projectReducer, - drugs: drugsReducer, - mirnas: mirnasReducer, - chemicals: chemicalsReducer, - bioEntityContents: bioEntityContentsReducer, - drawer: drawerReducer, - models: modelsReducer, - }, - preloadedState, - }); - - const Wrapper = ({ children }: WrapperProps): JSX.Element => ( - <Provider store={testStore}>{children}</Provider> - ); - - return { Wrapper, store: testStore }; -}; diff --git a/src/utils/testing/getReduxWrapperWithStore.tsx b/src/utils/testing/getReduxWrapperWithStore.tsx index 37e3cd5a..cf6097bd 100644 --- a/src/utils/testing/getReduxWrapperWithStore.tsx +++ b/src/utils/testing/getReduxWrapperWithStore.tsx @@ -1,4 +1,4 @@ -import { store } from '@/redux/store'; +import { RootState, StoreType } from '@/redux/store'; import { configureStore } from '@reduxjs/toolkit'; import { Provider } from 'react-redux'; import bioEntityContentsReducer from '@/redux/bioEntityContents/bioEntityContents.slice'; @@ -8,31 +8,17 @@ 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'; +import modelsReducer from '@/redux/models/models.slice'; interface WrapperProps { children: React.ReactNode; } -export type InitialStoreState = { - search?: SearchState; - project?: ProjectState; - drugs?: DrugsState; - mirnas?: MirnasState; - chemicals?: ChemicalsState; - bioEntityContents?: BioEntityContentsState; - drawer?: DrawerState; -}; +export type InitialStoreState = Partial<RootState>; type GetReduxWrapperUsingSliceReducer = (initialState?: InitialStoreState) => { Wrapper: ({ children }: WrapperProps) => JSX.Element; - store: typeof store; + store: StoreType; }; export const getReduxWrapperWithStore: GetReduxWrapperUsingSliceReducer = ( @@ -47,6 +33,7 @@ export const getReduxWrapperWithStore: GetReduxWrapperUsingSliceReducer = ( chemicals: chemicalsReducer, bioEntityContents: bioEntityContentsReducer, drawer: drawerReducer, + models: modelsReducer, }, preloadedState, }); -- GitLab