From 0ebba14ef1e1336889cddd00a1489fa24332311e Mon Sep 17 00:00:00 2001 From: Mateusz Bolewski <mateusz.bolewski@allegro.pl> Date: Fri, 10 Nov 2023 13:10:19 +0100 Subject: [PATCH] Feature/search drugs details --- .../DrugsAccordion.component.test.tsx | 2 +- .../AccordionsDetails.component.test.tsx | 63 +++++++++++++++++++ .../AccordionsDetails.component.tsx | 49 +++++++++++++++ .../AccordionsDetails.utils.tsx | 47 ++++++++++++++ .../PinsList/PinsList.component.tsx | 18 +++++- .../MirnaPinsListItem.component.tsx | 15 +++-- src/redux/drugs/drugs.selectors.ts | 16 ++++- yarn.lock | 6 +- 8 files changed, 200 insertions(+), 16 deletions(-) create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.test.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.tsx create mode 100644 src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.utils.tsx 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 ff2cc2db..ddba126b 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 @@ -33,7 +33,7 @@ describe('DrugsAccordion - component', () => { renderComponent({ drugs: { data: drugsFixture, loading: 'succeeded', error: { name: '', message: '' } }, }); - expect(screen.getByText('Drugs (2)')).toBeInTheDocument(); + expect(screen.getByText('Drugs (4)')).toBeInTheDocument(); }); it('should display loading indicator while waiting for drug 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 new file mode 100644 index 00000000..2f3c00b7 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.test.tsx @@ -0,0 +1,63 @@ +/* eslint-disable no-magic-numbers */ +import { drugsFixture } from '@/models/fixtures/drugFixtures'; +import { StoreType } from '@/redux/store'; +import { + InitialStoreState, + getReduxWrapperWithStore, +} from '@/utils/testing/getReduxWrapperWithStore'; +import { render, screen } from '@testing-library/react'; +import { AccordionsDetails } from './AccordionsDetails.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> + <AccordionsDetails pinsList={PINS_LIST} /> + </Wrapper>, + ), + { + store, + } + ); +}; + +describe('AccordionsDetails - component', () => { + it('should display name of drug', () => { + renderComponent(); + + const drugName = drugsFixture[0].name; + + expect(screen.getByText(drugName, { exact: false })).toBeInTheDocument(); + }); + it('should display description of drug', () => { + renderComponent(); + + const drugDescription = drugsFixture[0].description; + + expect(screen.getByText(drugDescription, { exact: false })).toBeInTheDocument(); + }); + it('should display synonyms of drug', () => { + renderComponent(); + + const firstDrugSynonym = drugsFixture[0].synonyms[0]; + const secondDrugSynonym = drugsFixture[0].synonyms[1]; + + expect(screen.getByText(firstDrugSynonym, { exact: false })).toBeInTheDocument(); + expect(screen.getByText(secondDrugSynonym, { exact: false })).toBeInTheDocument(); + }); + it('should display additional info about drug', () => { + renderComponent(); + + const drugAdditionalInfo = drugsFixture[0].bloodBrainBarrier; + + expect(screen.getByText(drugAdditionalInfo, { exact: false })).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 new file mode 100644 index 00000000..9b4bad36 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.component.tsx @@ -0,0 +1,49 @@ +import { + Accordion, + AccordionItem, + AccordionItemButton, + AccordionItemHeading, + AccordionItemPanel, +} from '@/shared/Accordion'; +import { PinItem } from '../PinsList/PinsList.types'; +import { + getAdditionalInfo, + getEntityDescriptions, + getEntityNames, + getEntitySynonyms, +} from './AccordionsDetails.utils'; + +interface AccordionsDetailsProps { + pinsList: PinItem[]; +} + +export const AccordionsDetails = ({ pinsList }: AccordionsDetailsProps): JSX.Element => { + return ( + <> + <Accordion allowZeroExpanded className="px-6"> + <AccordionItem> + <AccordionItemHeading> + <AccordionItemButton>Drug</AccordionItemButton> + </AccordionItemHeading> + <AccordionItemPanel>{getEntityNames(pinsList)}</AccordionItemPanel> + </AccordionItem> + <AccordionItem> + <AccordionItemHeading> + <AccordionItemButton>Description</AccordionItemButton> + </AccordionItemHeading> + <AccordionItemPanel>{getEntityDescriptions(pinsList)}</AccordionItemPanel> + </AccordionItem> + <AccordionItem> + <AccordionItemHeading> + <AccordionItemButton>Synonyms</AccordionItemButton> + </AccordionItemHeading> + <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> + </> + ); +}; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.utils.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.utils.tsx new file mode 100644 index 00000000..c5952708 --- /dev/null +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/AccordionsDetails/AccordionsDetails.utils.tsx @@ -0,0 +1,47 @@ +import { PinItem } from '../PinsList/PinsList.types'; + +export const getEntityNames = (pinsList: PinItem[]): string => { + let name = ''; + + pinsList.forEach(element => { + name += element.data.name; + }); + + return name; +}; + +export const getEntityDescriptions = (pinsList: PinItem[]): string => { + let description = ''; + + pinsList.forEach(element => { + if ('description' in element.data) { + description += element.data.description; + } + }); + + return description; +}; + +export const getEntitySynonyms = (pinsList: PinItem[]): string => { + let synonyms = ''; + + pinsList.forEach(element => { + if ('synonyms' in element.data) { + synonyms += element.data.synonyms.join(', '); + } + }); + + return synonyms; +}; + +export const getAdditionalInfo = (pinsList: PinItem[]): string => { + let additionalDetails = ''; + + pinsList.forEach(element => { + if ('bloodBrainBarrier' in element.data) { + additionalDetails += element.data.bloodBrainBarrier; + } + }); + + return additionalDetails; +}; 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 44bb49c8..6aeb854e 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx @@ -1,4 +1,5 @@ import { assertNever } from '@/utils/assertNever'; +import { AccordionsDetails } from '../AccordionsDetails/AccordionsDetails.component'; import { PinItem, PinType } from './PinsList.types'; import { MirnaPinsListItem } from './PinsListItem'; @@ -9,18 +10,29 @@ interface PinsListProps { export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => { switch (type) { + case 'drugs': + return ( + <div className="h-[calc(100vh-198px)] overflow-auto"> + <AccordionsDetails pinsList={pinsList} /> + <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 'bioEntity': return <div />; case 'chemicals': return <div />; - case 'drugs': - return <div />; case 'mirna': return ( <ul className="h-[calc(100vh-198px)] overflow-auto px-6 py-2"> {pinsList.map(result => { return result.data.targets.map(pin => ( - <MirnaPinsListItem key={pin.name} name={pin.name} pin={pin} /> + <MirnaPinsListItem key={pin.name} name={pin.name} type={type} pin={pin} /> )); })} </ul> diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/MirnaPinsListItem.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/MirnaPinsListItem.component.tsx index 2bb701d9..ebd97b20 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/MirnaPinsListItem.component.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsListItem/MirnaPinsListItem.component.tsx @@ -2,19 +2,22 @@ import { twMerge } from 'tailwind-merge'; import { Icon } from '@/shared/Icon'; import { MirnaItems } from '@/types/models'; import { getPinColor } from './PinsListItem.component.utils'; +import { PinType } from '../PinsList.types'; interface MirnaPinsListItemProps { name: string; + type: PinType; pin: MirnaItems; } -export const MirnaPinsListItem = ({ name, pin }: MirnaPinsListItemProps): JSX.Element => { +export const MirnaPinsListItem = ({ name, type, pin }: MirnaPinsListItemProps): JSX.Element => { return ( - <div className="mb-4 flex w-full flex-col gap-2 rounded-lg border-[1px] border-solid border-greyscale-500 p-4"> - <div className="flex w-full flex-row gap-2"> - <Icon name="pin" className={twMerge('mr-2 shrink-0', getPinColor('mirna'))} /> - <p className="min-w-fit">Full name: </p> - <p className="w-full font-bold">{name}</p> + <div className="mb-4 flex w-full flex-col gap-3 rounded-lg border-[1px] border-solid border-greyscale-500 p-4"> + <div className="flex w-full flex-row items-center gap-2"> + <Icon name="pin" className={twMerge('mr-2 shrink-0', getPinColor(type))} /> + <p> + Full name: <span className="w-full font-bold">{name}</span> + </p> </div> <ul className="leading-6"> <div className="font-bold">Elements:</div> diff --git a/src/redux/drugs/drugs.selectors.ts b/src/redux/drugs/drugs.selectors.ts index 7a1d3eac..af9a8d7f 100644 --- a/src/redux/drugs/drugs.selectors.ts +++ b/src/redux/drugs/drugs.selectors.ts @@ -5,6 +5,16 @@ import { createSelector } from '@reduxjs/toolkit'; export const drugsSelector = createSelector(rootSelector, state => state.drugs); export const loadingDrugsStatusSelector = createSelector(drugsSelector, state => state.loading); -export const numberOfDrugsSelector = createSelector(drugsSelector, state => - state.data ? state.data.length : SIZE_OF_EMPTY_ARRAY, -); +export const numberOfDrugsSelector = createSelector(drugsSelector, state => { + if (!state.data) { + return SIZE_OF_EMPTY_ARRAY; + } + + let numberOfDrugs = 0; + + state.data.forEach(element => { + numberOfDrugs += element.targets.length; + }); + + return numberOfDrugs; +}); diff --git a/yarn.lock b/yarn.lock index 9f446451..de1a94bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -847,9 +847,9 @@ "resolved" "https://registry.npmjs.org/@next/font/-/font-13.5.4.tgz" "version" "13.5.4" -"@next/swc-darwin-arm64@13.4.19": - "integrity" "sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==" - "resolved" "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz" +"@next/swc-darwin-x64@13.4.19": + "integrity" "sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==" + "resolved" "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz" "version" "13.4.19" "@nodelib/fs.scandir@2.1.5": -- GitLab