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

Merge branch 'development' of ssh://gitlab.lcsb.uni.lu:8022/minerva/frontend...

Merge branch 'development' of ssh://gitlab.lcsb.uni.lu:8022/minerva/frontend into feat/bioentity-from-map
parents 0dd63ec8 19bfa400
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...,!71Feat/bioentity from map
Pipeline #82502 passed
Showing
with 23 additions and 224 deletions
......@@ -3,13 +3,13 @@ import { bioEnititiesResultListSelector } from '@/redux/drawer/drawer.selectors'
import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { displayGroupedSearchResults } from '@/redux/drawer/drawer.slice';
import { searchValueSelector } from '@/redux/search/search.selectors';
import { currentModelNameSelector } from '@/redux/models/models.selectors';
import { BioEntitiesPinsList } from './BioEntitiesPinsList';
export const BioEntitiesResultsList = (): JSX.Element => {
const dispatch = useAppDispatch();
const bioEntityData = useAppSelector(bioEnititiesResultListSelector);
const searchValue = useAppSelector(searchValueSelector);
const mapName = useAppSelector(currentModelNameSelector);
const navigateToGroupedSearchResults = (): void => {
dispatch(displayGroupedSearchResults());
......@@ -17,11 +17,9 @@ export const BioEntitiesResultsList = (): JSX.Element => {
return (
<div>
<DrawerHeadingBackwardButton
title="BioEntity"
value={searchValue}
backwardFunction={navigateToGroupedSearchResults}
/>
<DrawerHeadingBackwardButton backwardFunction={navigateToGroupedSearchResults}>
{mapName}
</DrawerHeadingBackwardButton>
<BioEntitiesPinsList bioEnititesPins={bioEntityData} />
</div>
);
......
import { BioEntitiesAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion';
import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion';
import { ChemicalsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion';
import { MirnaAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion';
import { Accordion } from '@/shared/Accordion';
export const CLOSE_BUTTON_ROLE = 'close-drawer-button';
......@@ -14,7 +13,6 @@ export const GroupedSearchResults = (): JSX.Element => {
<BioEntitiesAccordion />
<DrugsAccordion />
<ChemicalsAccordion />
<MirnaAccordion />
</Accordion>
</div>
</div>
......
import { act, render, screen } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { Accordion } from '@/shared/Accordion';
import {
drawerSearchStepOneFixture,
drawerSearchMirnaStepTwoFixture,
} from '@/redux/drawer/drawerFixture';
import { mirnasFixture } from '@/models/fixtures/mirnasFixture';
import { MirnaAccordion } from './MirnaAccordion.component';
const SECOND_STEP = 2;
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<Accordion>
<MirnaAccordion />
</Accordion>
</Wrapper>,
),
{
store,
}
);
};
describe('MirnaAccordion - component', () => {
it('should display mirna number after succesfull mirna search', () => {
renderComponent({
mirnas: {
data: [
{
searchQueryElement: '',
loading: 'succeeded',
error: { name: '', message: '' },
data: mirnasFixture,
},
],
loading: 'succeeded',
error: { name: '', message: '' },
},
});
expect(screen.getByText('MiRNA (4)')).toBeInTheDocument();
});
it('should display loading indicator while waiting for mirna search response', () => {
renderComponent({
mirnas: { data: [], loading: 'pending', error: { name: '', message: '' } },
});
expect(screen.getByText('MiRNA (Loading...)')).toBeInTheDocument();
});
it('should navigate user to mirnas results list after clicking button', async () => {
const { store } = renderComponent({
mirnas: {
data: [
{
searchQueryElement: '',
loading: 'succeeded',
error: { name: '', message: '' },
data: mirnasFixture,
},
],
loading: 'succeeded',
error: { name: '', message: '' },
},
drawer: drawerSearchMirnaStepTwoFixture,
});
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();
});
});
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,
loadingMirnasStatusSelector,
} from '@/redux/mirnas/mirnas.selectors';
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"
onClick={onAccordionClick}
disabled={isPending || isDrugsEmpty}
>
MiRNA
{isPending && ' (Loading...)'}
{isSucceeded && ` (${mirnaNumber})`}
</AccordionItemButton>
</AccordionItemHeading>
</AccordionItem>
);
};
export { MirnaAccordion } from './MirnaAccordion.component';
import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture';
import { drugsFixture } from '@/models/fixtures/drugFixtures';
import { mirnasFixture } from '@/models/fixtures/mirnasFixture';
import { StoreType } from '@/redux/store';
import {
InitialStoreState,
......@@ -22,12 +21,6 @@ const CHEMICALS_PINS_LIST = chemicalsFixture.map(chemical => ({
data: chemical,
}));
const MIRNA_PINS_LIST = mirnasFixture.map(mirna => ({
id: mirna.id,
name: mirna.name,
data: mirna,
}));
const renderComponent = (
pinsList: PinItem[],
type: PinTypeWithNone,
......@@ -68,11 +61,6 @@ describe('PinsList - component ', () => {
expect(screen.getByTestId('accordions-details')).toBeInTheDocument();
});
it('should display list of mirnas targets', () => {
renderComponent(MIRNA_PINS_LIST, 'mirna');
expect(screen.getByTestId('pins-list')).toBeInTheDocument();
});
it('should not display list of bio enities when bioEntity is searched', () => {
renderComponent([], 'bioEntity');
......
......@@ -38,16 +38,6 @@ export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => {
</ul>
</div>
);
case 'mirna':
return (
<ul className="h-[calc(100vh-198px)] overflow-auto px-6 py-2" data-testid="pins-list">
{pinsList.map(result => {
return result.data.targets.map(pin => (
<PinsListItem key={pin.name} name={pin.name} type={type} pin={pin} />
));
})}
</ul>
);
case 'none':
return <div />;
default:
......
import { Chemical, Drug, Mirna } from '@/types/models';
import { Chemical, Drug } from '@/types/models';
import { PinType } from '@/types/pin';
export type PinItem = {
id: string | number;
name: string;
data: Drug | Chemical | Mirna;
data: Drug | Chemical;
};
export type PinTypeWithNone = PinType | 'none';
......@@ -4,13 +4,13 @@ import { twMerge } from 'tailwind-merge';
import { PinTypeWithNone } from '../PinsList.types';
import { getPinColor } from './PinsListItem.component.utils';
interface MirnaPinsListItemProps {
interface PinsListItemProps {
name: string;
type: PinTypeWithNone;
pin: PinDetailsItem;
}
export const PinsListItem = ({ name, type, pin }: MirnaPinsListItemProps): JSX.Element => {
export const PinsListItem = ({ name, type, pin }: PinsListItemProps): JSX.Element => {
return (
<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">
......
......@@ -5,7 +5,6 @@ export const getPinColor = (type: PinTypeWithNone): string => {
bioEntity: 'fill-primary-500',
drugs: 'fill-orange',
chemicals: 'fill-purple',
mirna: 'fill-pink',
none: 'none',
};
......
......@@ -60,8 +60,8 @@ describe('ResultsList - component ', () => {
it('should render results and navigation panel', () => {
renderComponent(INITIAL_STATE);
expect(screen.getByText('drugs:')).toBeInTheDocument();
expect(screen.getByText('aspirin')).toBeInTheDocument();
const headingText = screen.getByTestId('drawer-heading-text');
expect(headingText.textContent).toBe('drugs');
const fristDrugName = drugsFixture[0].targets[0].name;
const secondDrugName = drugsFixture[0].targets[1].name;
......
......@@ -3,14 +3,12 @@ import { resultListSelector, stepTypeDrawerSelector } from '@/redux/drawer/drawe
import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { displayGroupedSearchResults } from '@/redux/drawer/drawer.slice';
import { searchValueSelector } from '@/redux/search/search.selectors';
import { PinsList } from './PinsList';
export const ResultsList = (): JSX.Element => {
const dispatch = useAppDispatch();
const data = useAppSelector(resultListSelector);
const stepType = useAppSelector(stepTypeDrawerSelector);
const searchValue = useAppSelector(searchValueSelector);
const navigateToGroupedSearchResults = (): void => {
dispatch(displayGroupedSearchResults());
......@@ -18,11 +16,11 @@ export const ResultsList = (): JSX.Element => {
return (
<div>
<DrawerHeadingBackwardButton
title={stepType}
value={searchValue}
backwardFunction={navigateToGroupedSearchResults}
/>
<DrawerHeadingBackwardButton backwardFunction={navigateToGroupedSearchResults}>
<span className="capitalize" data-testid="drawer-heading-text">
{stepType}
</span>
</DrawerHeadingBackwardButton>
{data && <PinsList pinsList={data} type={stepType} />}
</div>
);
......
import { BIO_ENTITY, DRUGS_CHEMICALS_MIRNA } from '@/constants';
import { BIO_ENTITY, DRUGS_CHEMICALS } from '@/constants';
import { STEP } from '@/constants/searchDrawer';
import {
currentStepDrawerStateSelector,
......@@ -16,7 +16,7 @@ export const SearchDrawerWrapper = (): JSX.Element => {
const stepType = useSelector(stepTypeDrawerSelector);
const isBioEntityType = stepType === BIO_ENTITY;
const isChemicalsDrugsOrMirnaType = DRUGS_CHEMICALS_MIRNA.includes(stepType);
const isChemicalsOrDrugsType = DRUGS_CHEMICALS.includes(stepType);
return (
<>
......@@ -31,8 +31,8 @@ export const SearchDrawerWrapper = (): JSX.Element => {
<BioEntitiesResultsList />
</div>
)}
{/* 2nd step for drugs,chemicals,mirna */}
{currentStep === STEP.SECOND && isChemicalsDrugsOrMirnaType && (
{/* 2nd step for drugs,chemicals */}
{currentStep === STEP.SECOND && isChemicalsOrDrugsType && (
<div data-testid="search-second-step">
<ResultsList />
</div>
......
......@@ -29,7 +29,7 @@ describe('getBioEntitiesFeatures - subUtil', () => {
const bioEntities = bioEntititesContent.map(({ bioEntity }) => bioEntity);
const pointToProjection = getPointToProjection(Wrapper);
const pinTypes: PinType[] = ['bioEntity', 'drugs', 'chemicals', 'mirna'];
const pinTypes: PinType[] = ['bioEntity', 'drugs', 'chemicals'];
it.each(pinTypes)('should return array of instances of Feature with Style type=%s', type => {
const result = getBioEntitiesFeatures(bioEntities, {
......
......@@ -38,7 +38,7 @@ describe('getBioEntitySingleFeature - subUtil', () => {
const pointToProjection = getPointToProjection(Wrapper);
const value = 1448;
const pinTypes: PinType[] = ['bioEntity', 'drugs', 'chemicals', 'mirna'];
const pinTypes: PinType[] = ['bioEntity', 'drugs', 'chemicals'];
it.each(pinTypes)('should return instance of Feature with Style type=%s', type => {
const result = getBioEntitySingleFeature(bioEntity, {
......
......@@ -12,7 +12,6 @@ export const PINS_COLORS: Record<PinType, string> = {
drugs: '#F48C41',
chemicals: '#640CE3',
bioEntity: '#106AD7',
mirna: '#F1009F',
};
export const LINE_COLOR = '#00AAFF';
......
......@@ -6,4 +6,4 @@ export const BASE_NEW_API_URL = process.env.NEXT_PUBLIC_BASE_NEW_API_URL || '';
export const PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID || '';
export const ZOD_SEED = parseInt(process.env.ZOD_SEED || '123', 10);
export const BIO_ENTITY = 'bioEntity';
export const DRUGS_CHEMICALS_MIRNA = ['drugs', 'chemicals', 'mirna'];
export const DRUGS_CHEMICALS = ['drugs', 'chemicals'];
import { ZOD_SEED } from '@/constants';
import { mirnaSchema } from '@/models/mirnaSchema';
import { z } from 'zod';
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFixture } from 'zod-fixture';
export const mirnasFixture = createFixture(z.array(mirnaSchema), {
seed: ZOD_SEED,
array: { min: 2, max: 2 },
});
import { z } from 'zod';
import { targetSchema } from './targetSchema';
export const mirnaSchema = z.object({
id: z.string(),
name: z.string(),
targets: z.array(targetSchema),
});
......@@ -8,12 +8,6 @@ describe('api path', () => {
);
});
it('should return url string for miRNA', () => {
expect(apiPath.getMirnasStringWithQuery('hsa-miR-302b-3p')).toBe(
`projects/${PROJECT_ID}/miRnas:search?query=hsa-miR-302b-3p`,
);
});
it('should return url string for bio entity content', () => {
expect(
apiPath.getBioEntityContentsStringWithQuery({ searchQuery: 'park7', isPerfectMatch: false }),
......
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