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 cd116dd9e519875d002638bbea32f5a015d85781..29342db02a9a9422e1f01091e33d4eea5c3ec172 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 @@ -33,7 +33,7 @@ describe('DrugsAccordion - component', () => { renderComponent({ chemicals: { data: chemicalsFixture, loading: 'succeeded', error: { name: '', message: '' } }, }); - expect(screen.getByText('Chemicals (2)')).toBeInTheDocument(); + expect(screen.getByText('Chemicals (4)')).toBeInTheDocument(); }); it('should display loading indicator while waiting for chemicals search response', () => { renderComponent({ diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.test.tsx index 2f3c00b7a7e10e59098b8c5ea0d950de93fca5d2..e0c39d9b087437ec0d5bdd9e939ac59e91fb880b 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.test.tsx @@ -1,26 +1,38 @@ /* eslint-disable no-magic-numbers */ import { drugsFixture } from '@/models/fixtures/drugFixtures'; +import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture'; import { StoreType } from '@/redux/store'; import { InitialStoreState, getReduxWrapperWithStore, } from '@/utils/testing/getReduxWrapperWithStore'; import { render, screen } from '@testing-library/react'; +import { PinItem, PinType } from '../PinsList/PinsList.types'; import { AccordionsDetails } from './AccordionsDetails.component'; -const PINS_LIST = drugsFixture.map(drug => ({ +const DRUGS_PINS_LIST = drugsFixture.map(drug => ({ id: drug.id, name: drug.name, data: drug, })); -const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { +const CHEMICALS_PINS_LIST = chemicalsFixture.map(chemical => ({ + id: chemical.id.id, + name: chemical.name, + data: chemical, +})); + +const renderComponent = ( + pinsList: PinItem[], + type: PinType, + initialStoreState: InitialStoreState = {}, +): { store: StoreType } => { const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); return ( render( <Wrapper> - <AccordionsDetails pinsList={PINS_LIST} /> + <AccordionsDetails pinsList={pinsList} type={type} /> </Wrapper>, ), { @@ -31,21 +43,21 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St describe('AccordionsDetails - component', () => { it('should display name of drug', () => { - renderComponent(); + renderComponent(DRUGS_PINS_LIST, 'drugs'); const drugName = drugsFixture[0].name; expect(screen.getByText(drugName, { exact: false })).toBeInTheDocument(); }); it('should display description of drug', () => { - renderComponent(); + renderComponent(DRUGS_PINS_LIST, 'drugs'); const drugDescription = drugsFixture[0].description; expect(screen.getByText(drugDescription, { exact: false })).toBeInTheDocument(); }); it('should display synonyms of drug', () => { - renderComponent(); + renderComponent(DRUGS_PINS_LIST, 'drugs'); const firstDrugSynonym = drugsFixture[0].synonyms[0]; const secondDrugSynonym = drugsFixture[0].synonyms[1]; @@ -53,11 +65,20 @@ describe('AccordionsDetails - component', () => { expect(screen.getByText(firstDrugSynonym, { exact: false })).toBeInTheDocument(); expect(screen.getByText(secondDrugSynonym, { exact: false })).toBeInTheDocument(); }); - it('should display additional info about drug', () => { - renderComponent(); + it('should display blood brain barrier for drug', () => { + renderComponent(DRUGS_PINS_LIST, 'drugs'); const drugAdditionalInfo = drugsFixture[0].bloodBrainBarrier; expect(screen.getByText(drugAdditionalInfo, { exact: false })).toBeInTheDocument(); }); + it('should display direct evidence publications for chemicals', () => { + renderComponent(CHEMICALS_PINS_LIST, 'chemicals'); + + const chemicalsAdditionalInfo = chemicalsFixture[0].directEvidence + ? chemicalsFixture[0].directEvidence + : ''; + + expect(screen.getAllByText(chemicalsAdditionalInfo, { exact: false })[0]).toBeInTheDocument(); + }); }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.tsx index 9b4bad368a4a0efe4601c55db57a717a1b2490c9..0613843ec07bb7763c233b88987ba15d9749a67f 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.tsx @@ -5,7 +5,7 @@ import { AccordionItemHeading, AccordionItemPanel, } from '@/shared/Accordion'; -import { PinItem } from '../PinsList/PinsList.types'; +import { PinItem, PinType } from '../PinsList/PinsList.types'; import { getAdditionalInfo, getEntityDescriptions, @@ -15,15 +15,16 @@ import { interface AccordionsDetailsProps { pinsList: PinItem[]; + type: PinType; } -export const AccordionsDetails = ({ pinsList }: AccordionsDetailsProps): JSX.Element => { +export const AccordionsDetails = ({ pinsList, type }: AccordionsDetailsProps): JSX.Element => { return ( <> <Accordion allowZeroExpanded className="px-6"> <AccordionItem> <AccordionItemHeading> - <AccordionItemButton>Drug</AccordionItemButton> + <AccordionItemButton className="capitalize">{type}</AccordionItemButton> </AccordionItemHeading> <AccordionItemPanel>{getEntityNames(pinsList)}</AccordionItemPanel> </AccordionItem> @@ -40,10 +41,18 @@ export const AccordionsDetails = ({ pinsList }: AccordionsDetailsProps): JSX.Ele <AccordionItemPanel>{getEntitySynonyms(pinsList)}</AccordionItemPanel> </AccordionItem> </Accordion> - <div className="flex justify-between px-6 py-4 text-sm font-bold"> - <div>Blood brain barrier</div> - <div>{getAdditionalInfo(pinsList)}</div> - </div> + {type === 'drugs' && ( + <div className="flex justify-between px-6 py-4 text-sm font-bold"> + <div>Blood brain barrier</div> + <div>{getAdditionalInfo(pinsList, type)}</div> + </div> + )} + {type === 'chemicals' && ( + <div className="flex justify-between px-6 py-4 text-sm"> + <div className="font-bold">Direct Evidence Publications</div> + <div>{getAdditionalInfo(pinsList, type)}</div> + </div> + )} </> ); }; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.utils.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.utils.tsx index c59527084224d4b941aeb94a094b0739179be32d..2f29038f2a64d671fa5982fe52de5dc6f24366d7 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.utils.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.utils.tsx @@ -1,4 +1,4 @@ -import { PinItem } from '../PinsList/PinsList.types'; +import { PinItem, PinType } from '../PinsList/PinsList.types'; export const getEntityNames = (pinsList: PinItem[]): string => { let name = ''; @@ -34,14 +34,22 @@ export const getEntitySynonyms = (pinsList: PinItem[]): string => { return synonyms; }; -export const getAdditionalInfo = (pinsList: PinItem[]): string => { - let additionalDetails = ''; - - pinsList.forEach(element => { - if ('bloodBrainBarrier' in element.data) { - additionalDetails += element.data.bloodBrainBarrier; - } - }); - - return additionalDetails; +export const getAdditionalInfo = (pinsList: PinItem[], type: PinType): string => { + if (type === 'drugs') { + return pinsList + .map(element => ('bloodBrainBarrier' in element.data ? element.data.bloodBrainBarrier : '')) + .join(', '); + } + + if (type === 'chemicals') { + return pinsList + .map(element => + 'directEvidence' in element.data && element.data.directEvidence + ? element.data.directEvidence + : 'No annotations', + ) + .join(', '); + } + + return ''; }; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx index 6aeb854e74c1c8af9a129066d698ddc68e669a04..95f53e4fb1abecd24bf6aded02e864041e49b918 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx @@ -13,7 +13,7 @@ export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => { case 'drugs': return ( <div className="h-[calc(100vh-198px)] overflow-auto"> - <AccordionsDetails pinsList={pinsList} /> + <AccordionsDetails pinsList={pinsList} type={type} /> <ul className="px-6 py-2"> {pinsList.map(result => { return result.data.targets.map(pin => ( @@ -26,7 +26,18 @@ export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => { case 'bioEntity': return <div />; case 'chemicals': - return <div />; + return ( + <div className="h-[calc(100vh-198px)] overflow-auto"> + <AccordionsDetails pinsList={pinsList} type={type} /> + <ul className="px-6 py-2"> + {pinsList.map(result => { + return result.data.targets.map(pin => ( + <MirnaPinsListItem key={pin.name} name={pin.name} type={type} pin={pin} /> + )); + })} + </ul> + </div> + ); case 'mirna': return ( <ul className="h-[calc(100vh-198px)] overflow-auto px-6 py-2"> diff --git a/src/redux/chemicals/chemicals.selectors.ts b/src/redux/chemicals/chemicals.selectors.ts index 0fef7a3275e97b1a501223003e06e4cfe05f3f23..da031a64a254a41d1fa161156c5d5e2ae2a53fc5 100644 --- a/src/redux/chemicals/chemicals.selectors.ts +++ b/src/redux/chemicals/chemicals.selectors.ts @@ -9,6 +9,10 @@ export const loadingChemicalsStatusSelector = createSelector( state => state.loading, ); -export const numberOfChemicalsSelector = createSelector(chemicalsSelector, state => - state.data ? state.data.length : SIZE_OF_EMPTY_ARRAY, -); +export const numberOfChemicalsSelector = createSelector(chemicalsSelector, state => { + if (!state.data) { + return SIZE_OF_EMPTY_ARRAY; + } + + return state.data.length && state.data.map(e => e.targets.length)?.reduce((a, b) => a + b); +}); diff --git a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx index d932d2b580323d5eb5902438deb9ba435ddc247f..fb22d501fb80a1f16e756d3160b2fac75bab4d71 100644 --- a/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx +++ b/src/shared/Accordion/AccordionItemButton/AccordionItemButton.component.tsx @@ -1,3 +1,4 @@ +import { twMerge } from 'tailwind-merge'; import { AccordionItemButton as AIB } from 'react-accessible-accordion'; import './AccordionItemButton.style.css'; import { Variant } from './AccordionItemButton.types'; @@ -8,6 +9,7 @@ type AccordionItemButtonProps = { variant?: Variant; onClick?: () => void; disabled?: boolean; + className?: string; }; export const AccordionItemButton = ({ @@ -15,6 +17,7 @@ export const AccordionItemButton = ({ variant = 'expandable', onClick, disabled, + className, }: AccordionItemButtonProps): JSX.Element => { const ButtonIcon = getIcon(variant); @@ -23,7 +26,7 @@ export const AccordionItemButton = ({ <button onClick={onClick} disabled={disabled} - className="flex w-full flex-row flex-nowrap justify-between text-sm" + className={twMerge('flex w-full flex-row flex-nowrap justify-between text-sm', className)} type="button" data-testid="accordion-item-button" >