Skip to content
Snippets Groups Projects
Commit 0ebba14e authored by Mateusz Bolewski's avatar Mateusz Bolewski
Browse files

Feature/search drugs details

parent ea558ff3
No related branches found
No related tags found
2 merge requests!223reset the pin numbers before search results are fetch (so the results will be...,!51Feature/search drugs details
...@@ -33,7 +33,7 @@ describe('DrugsAccordion - component', () => { ...@@ -33,7 +33,7 @@ describe('DrugsAccordion - component', () => {
renderComponent({ renderComponent({
drugs: { data: drugsFixture, loading: 'succeeded', error: { name: '', message: '' } }, 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', () => { it('should display loading indicator while waiting for drug search response', () => {
renderComponent({ renderComponent({
......
/* 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();
});
});
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>
</>
);
};
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;
};
import { assertNever } from '@/utils/assertNever'; import { assertNever } from '@/utils/assertNever';
import { AccordionsDetails } from '../AccordionsDetails/AccordionsDetails.component';
import { PinItem, PinType } from './PinsList.types'; import { PinItem, PinType } from './PinsList.types';
import { MirnaPinsListItem } from './PinsListItem'; import { MirnaPinsListItem } from './PinsListItem';
...@@ -9,18 +10,29 @@ interface PinsListProps { ...@@ -9,18 +10,29 @@ interface PinsListProps {
export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => { export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => {
switch (type) { 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': case 'bioEntity':
return <div />; return <div />;
case 'chemicals': case 'chemicals':
return <div />; return <div />;
case 'drugs':
return <div />;
case 'mirna': case 'mirna':
return ( return (
<ul className="h-[calc(100vh-198px)] overflow-auto px-6 py-2"> <ul className="h-[calc(100vh-198px)] overflow-auto px-6 py-2">
{pinsList.map(result => { {pinsList.map(result => {
return result.data.targets.map(pin => ( 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> </ul>
......
...@@ -2,19 +2,22 @@ import { twMerge } from 'tailwind-merge'; ...@@ -2,19 +2,22 @@ import { twMerge } from 'tailwind-merge';
import { Icon } from '@/shared/Icon'; import { Icon } from '@/shared/Icon';
import { MirnaItems } from '@/types/models'; import { MirnaItems } from '@/types/models';
import { getPinColor } from './PinsListItem.component.utils'; import { getPinColor } from './PinsListItem.component.utils';
import { PinType } from '../PinsList.types';
interface MirnaPinsListItemProps { interface MirnaPinsListItemProps {
name: string; name: string;
type: PinType;
pin: MirnaItems; pin: MirnaItems;
} }
export const MirnaPinsListItem = ({ name, pin }: MirnaPinsListItemProps): JSX.Element => { export const MirnaPinsListItem = ({ name, type, pin }: MirnaPinsListItemProps): JSX.Element => {
return ( 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="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 gap-2"> <div className="flex w-full flex-row items-center gap-2">
<Icon name="pin" className={twMerge('mr-2 shrink-0', getPinColor('mirna'))} /> <Icon name="pin" className={twMerge('mr-2 shrink-0', getPinColor(type))} />
<p className="min-w-fit">Full name: </p> <p>
<p className="w-full font-bold">{name}</p> Full name: <span className="w-full font-bold">{name}</span>
</p>
</div> </div>
<ul className="leading-6"> <ul className="leading-6">
<div className="font-bold">Elements:</div> <div className="font-bold">Elements:</div>
......
...@@ -5,6 +5,16 @@ import { createSelector } from '@reduxjs/toolkit'; ...@@ -5,6 +5,16 @@ import { createSelector } from '@reduxjs/toolkit';
export const drugsSelector = createSelector(rootSelector, state => state.drugs); export const drugsSelector = createSelector(rootSelector, state => state.drugs);
export const loadingDrugsStatusSelector = createSelector(drugsSelector, state => state.loading); export const loadingDrugsStatusSelector = createSelector(drugsSelector, state => state.loading);
export const numberOfDrugsSelector = createSelector(drugsSelector, state => export const numberOfDrugsSelector = createSelector(drugsSelector, state => {
state.data ? state.data.length : SIZE_OF_EMPTY_ARRAY, if (!state.data) {
); return SIZE_OF_EMPTY_ARRAY;
}
let numberOfDrugs = 0;
state.data.forEach(element => {
numberOfDrugs += element.targets.length;
});
return numberOfDrugs;
});
...@@ -847,9 +847,9 @@ ...@@ -847,9 +847,9 @@
"resolved" "https://registry.npmjs.org/@next/font/-/font-13.5.4.tgz" "resolved" "https://registry.npmjs.org/@next/font/-/font-13.5.4.tgz"
"version" "13.5.4" "version" "13.5.4"
"@next/swc-darwin-arm64@13.4.19": "@next/swc-darwin-x64@13.4.19":
"integrity" "sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==" "integrity" "sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw=="
"resolved" "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz" "resolved" "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz"
"version" "13.4.19" "version" "13.4.19"
"@nodelib/fs.scandir@2.1.5": "@nodelib/fs.scandir@2.1.5":
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment