Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • minerva/frontend
1 result
Show changes
Commits on Source (5)
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',
};
......
......@@ -59,8 +59,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 }),
......